2015-06-19 17 views
5

Questa funzione non ha senso per me. Ho aggiunto dichiarazioni di stampa ovunque per capire cosa sta succedendo e non riesco ancora a capirlo. Sarei grato se qualcuno potesse spiegarmelo.esempio di educazione doppia ricorsiva

def f(s): 
    if len(s) <= 1: 
     return s 
    return f(f(s[1:])) + s[0] 

print(f("mat")) 

Questo è quello che vedo accadendo. Quindi iniziamo con una stringa di lunghezza 3 e ignoriamo l'istruzione if. Lavoriamo all'interno f (s [1:]) prima. Così ora abbiamo una stringa di lunghezza 2 ("at") che ignora di nuovo l'istruzione if e inserisce f (s [1]) che ci fornisce una stringa di lunghezza 1 ("t") che inserisce l'istruzione if e restituisce infine "t". Questo è dove il sentiero diventa freddo per me.

Dalle istruzioni di stampa, vedo una nuova stringa di lunghezza 2 viene creata e successiva "a" viene restituita. Il prodotto finale finisce per essere "atm". Ho ottenuto la "m" taggata alla fine grazie alla parte "+ s [0]" ma perché è "atm" e non "tam"?

Ho onestamente trascorso alcune ore su questo e non posso far piovere. Qualsiasi aiuto sarebbe apprezzato.

+1

Hai provato a usare carta e penna? Non è estremamente complesso, quindi ti consiglio di disegnare lo stack delle chiamate con lo stato corrente della variabile. Sarebbe meglio per te scoprirlo da solo che qualcuno te lo spiegasse. – amza

+1

@stazima - Dato che ci ha già passato qualche ora, penso che l'OP sarà meglio con spiegazioni più esplicite, altrimenti finirà per sbattere la testa contro lo stesso muro che sta colpendo finora. – rmunn

risposta

1

Versione corta: la a e t sono sempre scambiati due volte, in modo che il interno f("at") chiamata restituisce "ta", poi quella esterna f() chiamata viene passato "ta" e restituisce "at".

versione più lunga: non voglio scriverlo esplicitamente per voi, poiché non sarà imparare il più in quel modo, ma prendere in considerazione questa funzione, che è tutto equivalente:

def f2(s): 
    if len(s) <= 1: 
     return s 
    x = f2(s[1:]) 
    return f2(x) + s[0] 

print(f2("mat")) 

Quando si chiama f2("mat"), s[1:] è "at". Ora, cosa restituisce f2("at")? Inserisci quel valore (il valore di x) nell'espressione f2(x) + s[0] e guarda cosa ottieni.

Provare a scorrere i valori di f2("at") e f2("ta") e vedere se è possibile individuare ciò che sta accadendo. Se hai ancora bisogno di aiuto dopo altri 15-20 minuti di lavoro, commenta questa risposta e io la espanderò ancora.

+0

Posso vedere dove stai andando ma non sta ancora accadendo per me. Il mio hangup è legato al fatto che s [0] = "a" e quindi è uguale a "t". Quindi il primo ciclo restituisce "t" + s [0] ("a") = "ta". Poi passa di nuovo e inverte questi due. Ma non capisco perché passa una seconda volta e poi perché s [0] diventa di nuovo "m". – futevolei

+1

's [0]' non è una cosa, esiste all'interno della funzione, ma esiste una volta * ogni volta che f() 'è chiamato * e esiste solo * all'interno di una determinata chiamata *. Dato che 'f()' viene chiamato molte volte, ci sono molte versioni di 's [0]' che vengono create durante l'intera esecuzione del programma. Non "diventa nuovamente m", è un diverso s [0] in una chiamata f() diversa. – TessellatingHeckler

+0

@futevolei Il fatto che tu abbia fatto riferimento a ciò che sta accadendo come un "loop" indica un problema fondamentale nel modo in cui stai pensando a questo. Non c'è un ciclo. Ci sono solo chiamate di funzione.Se sai come tracciare ciò che accade in una chiamata di funzione, allora il fatto che la funzione faccia un'altra chiamata di funzione (a se stesso) non dovrebbe interferire con la tua capacità di rintracciare cosa sta succedendo nel complesso. Se pensi che sia un loop, tutte le scommesse sono disattivate. – pjs

5

Espandi l'intera procedura a lunghi passaggi completando le chiamate di funzione con quello che stanno facendo. Affrontare le parentesi dal più profondo/più incorporato prima. Gestire le chiamate di funzione prima dell'aggiunta.

Ho intenzione di ignorare le virgolette ovunque per chiarezza.

f(mat)   -> mat is 3 chars: 
        call f on at for f(at), and call f on that. 
        add m. 

f(f(at))+m  -> inner f(at), at is 2 chars: 
        call f on t for f(t), and call f on that. 
        add a. 

f(f(f(t))+a)+m -> innermost f(t) returns t. 

f(f(t)+a)+m  -> inner f(t) returns t as well. 

f(ta)+m   -> [here, the first f(f(at)) has been reduced to f(ta)] 
        ta is 2 chars: 
        call f on a for f(a), and call f on that. 
        add t. 

f(f(a))+t+m  -> inner f(a) returns a. 

f(a)+t+m   -> f(a) returns a as well. 

a + t + m  -> 

atm    -> everything reduces to atm. 
+0

Perché chiamare f due volte a? Non chiama f on at, quindi f sul valore restituito dalla prima chiamata? – stenci

+0

@stenci Lo fa, ed è quello che intendo con "call f twice". Forse non è un buon modo per descriverlo ... l'ho riscritto un po '. – TessellatingHeckler

1

Questa è in realtà una funzione sorprendentemente interessante, e posso capire perché ti sta confondendo. Suppongo che tu stia cercando di capire la funzione nel suo complesso, e che in realtà non funzionerà bene qui.

Al momento, ci sono due parti per questa funzione: gestire il caso in cui è vuoto o c'è solo una lettera nella stringa e gestire il caso in cui ci sono almeno due lettere nella stringa. Tuttavia, questo è ingannevole perché applica in modo efficace un'operazione diversa a seconda di quante lettere ci sono nella stringa!

Quindi, pensiamo alla funzione in modo non ricorsivo: se la stringa è troppo corta, basta restituire la stringa.Altrimenti, applicare una qualche funzione a due volte a tutti tranne il primo carattere della stringa, quindi aggiungere il primo carattere alla fine del risultato. Non pensarlo come se fosse la stessa funzione, basti pensare che si tratta di una funzione sconosciuta.

in codice:

def f(s): 
    if len(s) <= 1: 
     return s 
    return other_f(other_f(s[1:])) + s[0] 

nella tana del coniglio:

Quindi, come possiamo definire questo other_f? Vediamo quale comportamento deve avere per determinate lunghezze di stringa. Se len(s) è 2, allora sappiamo che s[1:] è un carattere, quindi other_f restituirà solo s[1:]. Nel codice:

def f2(s): # For when len(s)==2 
    #if statement is not used 
    #return other_f(other_f(s[1:])) + s[0] becomes 
    #return other_f(other_f(s[1])) + s[0] becomes 
    #return other_f(s[1]) + s[0] becomes 
    return s[1] + s[0] 

Semplicemente scambia le due lettere. Usiamo la stringa 'abc' per vedere più facilmente quello che sta succedendo con la prossima:

def f3(s): # For when len(s)==3 
    #if statement is not used 
    #return other_f(other_f(s[1:])) + s[0] becomes 
    #return f2(f2('bc')) + 'a' becomes 
    #return f2('cb') + 'a' becomes 
    #return 'bc' + 'a' 
    return s[1:] + s[0] 

Poiché la funzione applicata a 'bc' li scambia e si applica due volte, la funzione stessa disfa. Quindi in questo caso abbiamo semplicemente messo la prima lettera alla fine della stringa.

def f4(s): # For when len(s)==4 
    #return f3(f3(s[1:])) + s[0] becomes 
    #return f3(f3('bcd')) + 'a' becomes 
    #return f3('cdb') + 'a' becomes 
    #return f3('dbc') + 'a' becomes 
    #'dbca' 
    return s[3] + s[1:3] + s[0] # swap first and last letter 

def f5(s): # For when len(s)==5 
    #return f4(f4(s[1:])) + s[0] 
    #return f4(f4('bcde')) + 'a' 
    #swapping first and last letter twice just swaps then swaps back 
    #return 'bcde' + 'a' 
    return s[1:] + s[0] 

così sembra che abbiamo un bel modello di andare qui - se la stringa ha un numero pari di lettere, scambiare la prima e l'ultima lettera. Se ha un numero dispari di lettere, sposta la prima lettera fino alla fine!

... No. Quel modello termina con f5. Se esegui la funzione con stringhe come 'abcd ...' puoi facilmente vedere come ogni livello sposta le lettere in giro.

f | output 
---------- 
f6|'defbca' 
f7|'cdbfgea' 
f8|'cgefbhda' 
f9|'fecihbgda' 

Quindi, come si può vedere, per le stringhe più lunghe le lettere sono criptati intorno abbastanza bene diverso da primo carattere sempre fino a raggiungere la fine della stringa. Il modo migliore per pensarlo è che (con una singola riga di codice) sei riuscito a scrivere una funzione diversa per ogni lunghezza di stringa (con alcune funzioni che si comportano allo stesso modo, come f3 e f5). Ogni funzione dipende dalla funzione sopra di essa, quindi, poiché f6 in giù abbastanza bene randomizza la stringa, ogni ulteriore funzione dovrebbe anche randomizzare abbastanza bene la stringa.