it-swarm.it

In parole povere, cos'è la ricorsione?

L'idea della ricorsione non è molto comune nel mondo reale. Quindi, sembra un po 'confuso per i programmatori alle prime armi. Anche se, immagino, si abituano gradualmente al concetto. Quindi, quale può essere una bella spiegazione per loro di afferrare facilmente l'idea?

76
Gulshan

Per spiegare ricorsione , utilizzo una combinazione di spiegazioni diverse, di solito per entrambi tentare di:

  • spiegare il concetto,
  • spiegare perché è importante,
  • spiega come ottenerlo.

Per cominciare, Wolfram | Alpha lo definisce in termini più semplici di Wikipedia :

Un'espressione tale che ogni termine è generato ripetendo una particolare operazione matematica.


Matematica

Se il tuo studente (o la persona che spieghi anche tu, d'ora in poi dirò studente) ha almeno un background matematico, hanno ovviamente già incontrato ricorsioni studiando le serie e la loro nozione di ricorsività e loro relazione di ricorrenza .

Un ottimo modo per iniziare è quindi dimostrare con una serie e dire che è semplicemente ciò che riguarda la ricorsione:

  • una funzione matematica ...
  • ... che si chiama per calcolare un valore corrispondente a un n-esimo elemento ...
  • ... e che definisce alcuni confini.

Di solito, o ottieni un "huh huh, whatev '" nella migliore delle ipotesi perché ancora non lo usano, o più probabilmente solo un russare molto profondo.


Esempi di codifica

Per il resto, in realtà è una versione dettagliata di ciò che ho presentato nell'addendum di la mia risposta per domanda che hai indicato riguardo ai puntatori (bad pun).

In questa fase, i miei studenti di solito sanno come stampare qualcosa sullo schermo. Supponendo che stiamo usando C, sanno come stampare un singolo carattere usando write o printf. Conoscono anche i circuiti di controllo.

Di solito ricorro ad alcuni problemi di programmazione ripetitivi e semplici fino a quando non lo ottengono:

  • l'operazione fattoriale ,
  • una stampante alfabetica,
  • una stampante alfabetica invertita,
  • l'operazione esponenziale .

fattoriale

Il fattoriale è un concetto matematico molto semplice da capire e l'implementazione è molto vicina alla sua rappresentazione matematica. Tuttavia, all'inizio potrebbero non capirlo.

Recursive Definition of the Factorial Operation

Alfabeti

La versione alfabetica è interessante per insegnare loro a pensare all'ordinamento delle loro dichiarazioni ricorsive. Come con i puntatori, ti lanceranno semplicemente delle linee in modo casuale. Il punto è portarli alla realizzazione che un ciclo può essere invertito modificando le condizioni [~ # ~] o [~ # ~] di semplicemente invertendo l'ordine delle istruzioni nella tua funzione. Ecco dove la stampa dell'alfabeto aiuta, poiché è qualcosa di visivo per loro. Basta che scrivano una funzione che stamperà un carattere per ogni chiamata e si chiama ricorsivamente per scrivere quello successivo (o precedente).

Ventilatori FP, salta il fatto che stampare materiale sul flusso di output è un effetto collaterale per ora ... Non diventiamo troppo fastidiosi sul fronte FP. (Ma se usi una lingua con il supporto dell'elenco, sentiti libero di concatenare un elenco ad ogni iterazione e semplicemente stampare il risultato finale. Ma di solito li avvio con C, che purtroppo non è il migliore per questo tipo di problemi e concetti) .

Elevamento

Il problema esponenziale è leggermente più difficile ( in questa fase dell'apprendimento). Ovviamente il concetto è esattamente lo stesso di un fattoriale e non c'è complessità aggiunta ... tranne per il fatto che hai più parametri. E questo di solito è abbastanza per confondere le persone e buttarle via all'inizio.

La sua forma semplice:

Simple Form of the Exponentiation Operation

può essere espresso in questo modo dalla ricorrenza:

Recurrence Relation for the Exponentiation Operation

Harder

Una volta che questi semplici problemi sono stati mostrati E reimplementati nei tutorial, puoi dare esercizi leggermente più difficili (ma molto classici):

Nota: alcuni di questi non sono in realtà più difficili ... Si limitano ad affrontare il problema esattamente dalla stessa angolazione, o leggermente diversa. Ma la pratica rende perfetti.


Helpers

Un riferimento

Alcune letture non fanno mai male. Bene all'inizio, e si sentiranno ancora più persi. È il genere di cose che cresce su di te e che rimane nella parte posteriore della testa fino a quando un giorno ti accorgi di averlo finalmente ottenuto. E poi ripensi a queste cose che leggi. Le pagine ricorsione , ricorsione in Informatica e relazione di ricorrenza su Wikipedia farebbero per ora.

Livello/profondità

Supponendo che i tuoi studenti non abbiano molta esperienza di programmazione, fornisci stub di codice. Dopo i primi tentativi, assegnare loro una funzione di stampa in grado di visualizzare il livello di ricorsione. La stampa del valore numerico del livello aiuta.

Il diagramma Stack-as-Drawers

Anche il rientro di un risultato stampato (o dell'output del livello) aiuta, poiché fornisce un'altra rappresentazione visiva di ciò che il programma sta facendo, aprendo e chiudendo contesti di stack come i cassetti o le cartelle in un Esplora file system.

Acronimi ricorsivi

Se il tuo studente è già un po 'esperto nella cultura del computer, potrebbe già utilizzare alcuni progetti/software con nomi usando acronimi ricorsivi . È una tradizione che va in giro da qualche tempo, specialmente in GNU. Alcuni esempi includono:

Ricorsivo:

  • GNU - "GNU's Not Unix"
  • Nagios - "Nagios non vuole insistere sulla santità"
  • PHP - "PHP Hypertext Preprocessor" (e originariamente "Personal Home Page")
  • Vino - "Il vino non è un emulatore"
  • Zile - "Zile Is Lossy Emacs"

Reciprocamente ricorsivo:

  • HURD - "HIRD of Unix-Replacing Daemons" (dove HIRD è "HURD of Interfaces che rappresenta Depth")

Fagli provare a inventare il loro.

Allo stesso modo, ci sono molte occorrenze di umorismo ricorsivo, come ricerca ricorsiva di Google correzione. Per ulteriori informazioni sulla ricorsione, leggi questa risposta .


Insidie ​​e ulteriore apprendimento

Alcuni problemi con cui le persone di solito lottano e per i quali è necessario conoscere le risposte.

Perché, oh Dio, perché ???

Perché dovresti farlo? Un motivo valido ma non ovvio è che spesso è più semplice esprimere un problema in questo modo. Una ragione non così buona ma ovvia è che spesso ci vuole meno battitura a macchina (non farli sentire veramente l33t solo per usare la ricorsione però ...).

Alcuni problemi sono sicuramente più facili da risolvere quando si utilizza un approccio ricorsivo. In genere, qualsiasi problema che è possibile risolvere utilizzando un paradigma Divide and Conquer si adatta a un algoritmo di ricorsione multi-ramificato.

Cosa c'è di nuovo N ??

Perché il mio n o (qualunque sia il nome della tua variabile) ogni volta è diverso? I principianti di solito hanno un problema a capire cosa sono una variabile e un parametro e come le cose denominate n nel tuo programma possono avere valori diversi. Quindi ora se questo valore è nel circuito di controllo o ricorsione, è ancora peggio! Sii gentile e non usare gli stessi nomi di variabili ovunque, e chiarisci che i parametri sono solo variabili .

Condizioni finali

Come posso determinare la mia condizione finale? È facile, basta far loro dire i passi ad alta voce. Ad esempio per il fattoriale iniziare da 5, quindi 4, quindi ... fino a 0.

Il diavolo è nei dettagli

Non parlare alle prime cose come ottimizzazione della coda . Lo so, lo so, il TCO è bello, ma all'inizio non gliene importa. Concedi loro un po 'di tempo per avvolgere la testa attorno al processo in un modo che funzioni per loro. Sentiti libero di frantumare il loro mondo più tardi, ma concedi loro una pausa.

Allo stesso modo, non parlare direttamente dalla prima lezione di call stack e del suo consumo di memoria e ... beh ... il stack overflow . Spesso insegno agli studenti privatamente che mi mostrano lezioni in cui hanno 50 diapositive su tutto c'è da sapere sulla ricorsione quando riescono a malapena a scrivere correttamente un ciclo in questa fase. Questo è un buon esempio di come un riferimento ti aiuterà più tardi ma in questo momento solo confonde tu profondamente.

Ma per favore, a tempo debito, chiarisci che ci sono ragioni per percorrere la strada iterativa o ricorsiva .

Ricorsione reciproca

Abbiamo visto che le funzioni possono essere ricorsive e anche che possono avere più punti di chiamata (8 regine, Hanoi, Fibonacci o persino un algoritmo di esplorazione per un dragamine). Ma che dire di chiamate reciprocamente ricorsive ? Inizia anche qui con la matematica. f(x) = g(x) + h(x) dove g(x) = f(x) + l(x) e h e l fanno semplicemente cose.

Iniziare con solo serie matematiche semplifica la scrittura e l'implementazione poiché il contratto è chiaramente definito dalle espressioni. Ad esempio, Hofstadter Female and Male Sequences :

Hofstadter's Male and Female Sequences

Tuttavia, in termini di codice, va notato che l'implementazione di una soluzione reciprocamente ricorsiva porta spesso alla duplicazione del codice e dovrebbe piuttosto essere semplificata in una singola forma ricorsiva (Vedi Peter Norvig 's Risolvere ogni puzzle di Sudok .

111
haylem

La chiamata di una funzione all'interno di quella stessa funzione.

59
ChaosPandion

La ricorsione è una funzione che si chiama da sola.

Come usarlo, quando usarlo e come evitare un cattivo design sono importanti da sapere, il che richiede di provarlo da soli e capire cosa succede.

La cosa più importante che devi sapere è stare molto attenti a non fare un ciclo che non finisce mai. La risposta di pramodc84 alla tua domanda ha questo difetto: non finisce mai ...
Una funzione ricorsiva deve sempre verificare la presenza di una condizione per determinare se deve richiamare nuovamente se stessa.

L'esempio più classico di usare la ricorsione è lavorare con un albero senza limiti statici in profondità. Questa è un'attività che è necessario utilizzare la ricorsione.

27
awe

La programmazione ricorsiva è il processo di riduzione progressiva di un problema per semplificare la risoluzione delle versioni di se stesso.

Ogni funzione ricorsiva tende a:

  1. prendere un elenco da elaborare, o qualche altra struttura o dominio problematico
  2. gestire l'attuale punto/passo
  3. chiamarsi sul resto/i/sottodominio/i
  4. combinare o utilizzare i risultati del lavoro del sottodominio

Quando il passaggio 2 è precedente al 3 e quando il passaggio 4 è banale (una concatenazione, somma o nulla), ciò abilita ricorsione della coda . Il passaggio 2 spesso deve venire dopo il passaggio 3, poiché i risultati del sottodominio (i) del problema potrebbero essere necessari per completare il passaggio corrente.

Prendi l'attraversamento di un albero binario diritto. La traversata può essere effettuata in pre-ordine, in ordine o in ordine postale, a seconda di ciò che è richiesto.

   B
A     C

Pre-ordine: B A C

traverse(tree):
    visit the node
    traverse(left)
    traverse(right)

In ordine: A B C

traverse(tree):
    traverse(left)
    visit the node
    traverse(right)

Post-ordine: A C B

traverse(tree):
    traverse(left)
    traverse(right)
    visit the node

Molti problemi ricorsivi sono casi specifici di un'operazione mappa o piega - la comprensione di queste due operazioni può portare a una comprensione significativa di buoni casi d'uso per la ricorsione.

21
Orbling

L'OP ha affermato che la ricorsione non esiste nel mondo reale, ma mi permetto di dissentire.

Prendiamo "l'operazione" nel mondo reale per tagliare una pizza. Hai tolto la pizza dal forno e per servirla devi tagliarla a metà, quindi tagliare quelle metà a metà, quindi tagliare nuovamente quelle metà risultanti a metà.

L'operazione di tagliare la pizza che esegui ancora e ancora finché non ottieni il risultato che desideri (il numero di fette). E per amor di argomenti diciamo che una pizza non tagliata è una fetta in sé.

Ecco un esempio in Ruby:

 def cut_pizza (isting_slices, desiderata_slice) 
 seisting_slice! = desiderata_slice 
 # non abbiamo ancora abbastanza fette per sfamare tutti, quindi 
 # siamo tagliando le fette di pizza, raddoppiando così il loro numero 
 new_slices =isting_slices * 2 
 # e questa è la chiamata ricorsiva 
 cut_pizza (new_slices, wanted_slices) 
 else 
 # abbiamo il numero desiderato di sezioni, quindi restituiamo 
 # qui invece di continuare a ricorrere 
 restituireisting_slices 
 end 
 end 
 
 pizza = 1 # una pizza intera, 'una fetta' 
 cut_pizza (pizza, 8) # => avremo 8 

Quindi l'operazione nel mondo reale sta tagliando una pizza, e la ricorsione sta facendo la stessa cosa ancora e ancora fino a quando non hai quello che vuoi.

Le operazioni che scoprirai che è possibile implementare con funzioni ricorsive sono:

  • Calcolo dell'interesse composto per un numero di mesi.
  • Ricerca di un file su un file system (perché i file system sono alberi a causa delle directory).
  • Qualunque cosa implichi il lavoro con gli alberi in generale, immagino.

Consiglio di scrivere un programma per cercare un file in base al nome del file e provare a scrivere una funzione che si chiama fino a quando non viene trovata, la firma sarebbe simile a questa:

find_file_by_name(file_name_we_are_looking_for, path_to_look_in)

Quindi potresti chiamarlo così:

find_file_by_name('httpd.conf', '/etc') # damn it i can never find Apache's conf

Secondo me è semplicemente la programmazione della meccanica, un modo per rimuovere abilmente la duplicazione. Puoi riscriverlo usando le variabili, ma questa è una soluzione "più bella". Non c'è nulla di misterioso o difficile al riguardo. Scriverai un paio di funzioni ricorsive, farà clic e huzzah un altro trucco meccanico nella tua casella degli strumenti di programmazione.

credito extra Il cut_pizza l'esempio sopra ti darà un errore di livello di stack troppo profondo se lo chiedi per un numero di sezioni che non è una potenza di 2 (cioè 2 o 4 o 8 o 16). Puoi modificarlo in modo che se qualcuno chiede 10 sezioni non funzionerà per sempre?

20
Ryan Allen

Ok, cercherò di mantenerlo semplice e conciso.

Le funzioni ricorsive sono funzioni che si definiscono. La funzione ricorsiva consiste di tre cose:

  1. Logica
  2. Una chiamata a se stesso
  3. Quando terminare.

Il modo migliore per scrivere metodi ricorsivi è pensare al metodo che si sta tentando di scrivere come un semplice esempio gestendo solo un ciclo del processo su cui si desidera ripetere l'iterazione, quindi aggiungere la chiamata al metodo stesso e aggiungere quando si desidera terminare. Il modo migliore per imparare è esercitarsi come tutte le cose.

Dal momento che questo è il sito web dei programmatori, mi asterrò dalla scrittura del codice, ma qui è un buon link

se hai preso quella battuta hai capito cosa significa ricorsione.

16
dustyprogrammer

La ricorsione è uno strumento che un programmatore può utilizzare per invocare una chiamata di funzione su se stessa. La sequenza di Fibonacci è l'esempio da manuale di come viene utilizzata la ricorsione.

La maggior parte del codice ricorsivo, se non tutto, può essere espresso come funzione iterativa, ma di solito è disordinato. Buoni esempi di altri programmi ricorsivi sono Strutture dati come alberi, albero di ricerca binario e persino quicksort.

La ricorsione viene utilizzata per rendere il codice meno sciatto, tenere presente che di solito è più lento e richiede più memoria.

6
Bryan Harrington

Mi piace usare questo:

Come vai al negozio?

Se sei all'ingresso del negozio, basta attraversarlo. Altrimenti, fai un passo, poi cammina fino al negozio.

È fondamentale includere tre aspetti:

  • Un banale caso di base
  • Risolvere una piccola parte del problema
  • Risolvere il resto del problema in modo ricorsivo

In realtà usiamo molto la ricorsione nella vita quotidiana; semplicemente non la pensiamo così.

5
Barry Brown

Il miglior esempio a cui vorrei farti riferimento è il linguaggio di programmazione C di K & R. In quel libro (e sto citando dalla memoria), la voce nella pagina dell'indice per la ricorsione (da sola) elenca la pagina attuale in cui parlano di ricorsione e anche la pagina dell'indice.

3
Kanini

Josh K ha già menzionato le bambole Matroshka . Supponi di voler imparare qualcosa che solo la bambola più corta conosce. Il problema è che non puoi davvero parlarle direttamente, perché in origine vive dentro la bambola più alta che nella prima foto è posizionata alla sua sinistra. Questa struttura va così (una bambola vive all'interno della bambola più alta) fino a finire solo con la più alta.

Quindi l'unica cosa che puoi fare è porre la tua domanda alla bambola più alta. La bambola più alta (che non conosce la risposta) dovrà passare la tua domanda alla bambola più corta (che nella prima foto è alla sua destra). Dal momento che anche lei non ha la risposta, deve chiedere alla prossima bambola più corta. Questo andrà così fino a quando il messaggio non raggiungerà la bambola più corta. La bambola più corta (che è l'unica a conoscere la risposta segreta) passerà la risposta alla prossima bambola più alta (trovata alla sua sinistra), che la passerà alla successiva bambola più alta ... e questo continuerà fino alla risposta raggiunge la sua destinazione finale, che è la bambola più alta e finalmente ... tu :)

Questo è ciò che fa davvero la ricorsione. Una funzione/metodo si chiama fino a ottenere la risposta prevista. Ecco perché quando scrivi il codice ricorsivo è molto importante decidere quando terminare la ricorsione.

Non è la migliore spiegazione, ma si spera che aiuti.

2
sakisk

Ricorsione n. - Un modello di progettazione algoritmica in cui un'operazione è definita in termini di se stessa.

L'esempio classico è trovare il fattoriale di un numero, n !. 0! = 1, e per qualsiasi altro numero naturale N, il fattoriale di N è il prodotto di tutti i numeri naturali minori o uguali a N. Quindi, 6! = 6 * 5 * 4 * 3 * 2 * 1 = 720. Questa definizione di base ti consentirebbe di creare una soluzione iterativa semplice:

int Fact(int degree)
{
    int result = 1;
    for(int i=degree; i>1; i--)
       result *= i;

    return result;
}

Tuttavia, esaminare nuovamente l'operazione. 6! = 6 * 5 * 4 * 3 * 2 * 1. Con la stessa definizione, 5! = 5 * 4 * 3 * 2 * 1, nel senso che possiamo dire 6! = 6 * (5!). A sua volta, 5! = 5 * (4!) E così via. In questo modo, riduciamo il problema a un'operazione eseguita sul risultato di tutte le operazioni precedenti. Questo alla fine si riduce a un punto, chiamato caso base, in cui il risultato è noto per definizione. Nel nostro caso, 0! = 1 (nella maggior parte dei casi potremmo anche dire che 1! = 1). Nell'informatica, ci è spesso permesso di definire algoritmi in un modo molto simile, facendo in modo che il metodo si chiami e passi un input più piccolo, riducendo così il problema attraverso molte ricorsioni a un caso base:

int Fact(int degree)
{
    if(degree==0) return 1; //the base case; 0! = 1 by definition
    else return degree * Fact(degree -1); //the recursive case; N! = N*(N-1)!
}

Ciò può, in molte lingue, essere ulteriormente semplificato utilizzando l'operatore ternario (a volte visto come una funzione Iif in lingue che non forniscono l'operatore in quanto tale):

int Fact(int degree)
{
    //reads equivalently to the above, but is concise and often optimizable
    return degree==0 ? 1: degree * Fact(degree -1);
}

Vantaggi:

  • Espressione naturale - per molti tipi di algoritmi, questo è un modo molto naturale per esprimere la funzione.
  • LOC ridotto: spesso è molto più conciso definire una funzione in modo ricorsivo.
  • Velocità: in alcuni casi, a seconda della lingua e dell'architettura del computer, la ricorsione di un algoritmo è più rapida della soluzione iterativa equivalente, in genere perché effettuare una chiamata di funzione è un'operazione più veloce a livello hardware rispetto alle operazioni e all'accesso alla memoria necessari per eseguire un ciclo iterativo.
  • Divisibilità - Molti algoritmi ricorsivi sono della mentalità "dividi e conquista"; il risultato dell'operazione è una funzione del risultato della stessa operazione eseguita su ciascuna delle due metà dell'ingresso. Ciò consente di dividere il lavoro in due per ogni livello e, se disponibile, è possibile assegnare l'altra metà a un'altra "unità di esecuzione" da elaborare. Questo è generalmente più difficile o impossibile con un algoritmo iterativo.

Svantaggi:

  • Richiede comprensione - Devi semplicemente "afferrare" il concetto di ricorsione per capire cosa sta succedendo e quindi scrivere e mantenere efficaci algoritmi ricorsivi. Altrimenti sembra solo magia nera.
  • Dipende dal contesto - Se la ricorsione è una buona idea o meno dipende da quanto elegantemente l'algoritmo possa essere definito in termini di se stesso. Sebbene sia possibile creare, ad esempio, un SelectionSort ricorsivo, l'algoritmo iterativo è in genere il più comprensibile.
  • Trades RAM accesso per stack di chiamate - In genere, le chiamate di funzione sono più economiche dell'accesso alla cache, il che può rendere la ricorsione più veloce dell'iterazione. Tuttavia, di solito c'è un limite alla profondità dello stack di chiamate che può causare ricorsione dell'errore in cui funzionerà un algoritmo iterativo.
  • Ricorsione infinita - Devi sapere quando fermarti. È anche possibile l'iterazione infinita, ma i costrutti di loop coinvolti sono generalmente più facili da capire e quindi da debug.
2
KeithS

L'esempio che uso è un problema che ho affrontato nella vita reale. Hai un contenitore (come uno zaino grande che intendi portare in viaggio) e vuoi conoscere il peso totale. Hai nel contenitore due o tre oggetti sciolti e alcuni altri contenitori (diciamo, sacchi di roba). Il peso del contenitore totale è ovviamente il peso del contenitore vuoto più il peso di tutto ciò che contiene. Per gli articoli sfusi, puoi semplicemente pesarli, e per i sacchi di roba potresti semplicemente pesarli o potresti dire "bene il peso di ogni sacco è il peso del contenitore vuoto più il peso di tutto ciò che contiene". E poi continui ad andare in contenitori in contenitori e così via fino ad arrivare a un punto in cui ci sono solo oggetti sfusi in un contenitore. Questa è la ricorsione.

Potresti pensare che non accada mai nella vita reale, ma immagina di provare a contare o sommare gli stipendi di persone in una particolare azienda o divisione, che ha una miscela di persone che lavorano solo per l'azienda, persone in divisioni, quindi in le divisioni ci sono dipartimenti e così via. O le vendite in un paese che ha regioni, alcune delle quali hanno sottoregioni, ecc. Ecc. Questo tipo di problemi si verificano continuamente negli affari.

1
Kate Gregory

La ricorsione può essere utilizzata per risolvere molti problemi di conteggio. Ad esempio, supponi di avere un gruppo di n persone a una festa (n> 1) e che tutti stringano la mano di tutti gli altri esattamente una volta. Quante strette di mano avvengono? Potresti sapere che la soluzione è C (n, 2) = n (n-1)/2, ma puoi risolvere ricorsivamente come segue:

Supponiamo che ci siano solo due persone. Quindi (per ispezione) la risposta è ovviamente 1.

Supponiamo di avere tre persone. Individua una persona e nota che si stringe la mano con altre due persone. Dopo di che devi contare solo le strette di mano tra le altre due persone. L'abbiamo già fatto proprio ora ed è 1. Quindi la risposta è 2 + 1 = 3.

Supponiamo di avere n persone. Seguendo la stessa logica di prima, è (n-1) + (numero di strette di mano tra n-1 persone). Espandendo, otteniamo (n-1) + (n-2) + ... + 1.

Espresso come funzione ricorsiva,

f (2) = 1
f (n) = n-1 + f (n-1), n> 2

0
Mitch Schwartz

Ecco un esempio del mondo reale per la ricorsione.

Fagli immaginare che abbiano una collezione a fumetti e mescolerai tutto in un grande mucchio. Attenzione: se hanno davvero una collezione, potrebbero ucciderti all'istante quando menzioni semplicemente l'idea di farlo.

Ora lascia che ordinino questa grande pila di fumetti non ordinati con l'aiuto di questo manuale:

Manual: How to sort a pile of comics

Check the pile if it is already sorted. If it is, then done.

As long as there are comics in the pile, put each one on another pile, 
ordered from left to right in ascending order:

    If your current pile contains different comics, pile them by comic.
    If not and your current pile contains different years, pile them by year.
    If not and your current pile contains different tenth digits, pile them 
    by this digit: Issue 1 to 9, 10 to 19, and so on.
    If not then "pile" them by issue number.

Refer to the "Manual: How to sort a pile of comics" to separately sort each
of the new piles.

Collect the piles back to a big pile from left to right.

Done.

La cosa bella qui è: quando si tratta di singoli problemi, hanno l'intero "stack frame" con le pile locali visibili davanti a loro sul terreno. Fornisci loro più stampe del manuale e mettine da parte ogni livello di pila con un segno in cui ti trovi attualmente a questo livello (cioè lo stato delle variabili locali), in modo da poter continuare lì su ogni Fine.

Questo è fondamentalmente la ricorsione: eseguire lo stesso processo, solo a un livello di dettaglio più preciso, più ci si addentra.

0
Secure

Nella vita (al contrario di un programma per computer) la ricorsione avviene raramente sotto il nostro controllo diretto, perché può essere fonte di confusione. Inoltre, la percezione tende a riguardare gli effetti collaterali, piuttosto che essere funzionalmente pura, quindi se si verifica una ricorsione non si può notare.

Tuttavia, la ricorsione avviene qui nel mondo. Un sacco.

Un buon esempio è (una versione semplificata di) il ciclo dell'acqua:

  • Il sole riscalda il lago
  • L'acqua sale verso il cielo e forma le nuvole
  • Le nuvole si spostano su una montagna
  • Sulla montagna l'aria diventa troppo fredda per trattenere la loro umidità
  • Piove
  • Si forma un fiume
  • L'acqua nel fiume scorre nel lago

Questo è un ciclo che fa succedere di nuovo se stesso. È ricorsivo.

Un altro posto in cui puoi ricorrere è l'inglese (e la lingua umana in generale). All'inizio potresti non riconoscerlo, ma il modo in cui possiamo generare una frase è ricorsivo, perché le regole ci consentono di incorporare un'istanza di un simbolo in un'altra istanza dello stesso simbolo.

Da The Language Instinct di Steven Pinker:

se o la ragazza mangia gelato o la ragazza mangia caramelle, allora il ragazzo mangia hot dog

Questa è un'intera frase che contiene altre frasi intere:

la ragazza mangia il gelato

la ragazza mangia caramelle

il ragazzo mangia hot dog

L'atto di comprendere l'intera frase implica la comprensione di frasi più piccole, che usano la stessa serie di trucchi mentali per essere comprese come la frase completa.

Per comprendere la ricorsione dal punto di vista della programmazione è più semplice esaminare un problema che può essere risolto con la ricorsione e capire perché dovrebbe essere e cosa significa che devi fare.

Per l'esempio userò la più grande funzione divisore comune, o gcd in breve.

Hai i tuoi due numeri a e b. Per trovare il loro gcd (supponendo che nessuno dei due sia 0) è necessario verificare se a è uniformemente divisibile in b. Se è allora b è il gcd, altrimenti devi controllare il gcd di b e il resto di a/b.

Dovresti già essere in grado di vedere che questa è una funzione ricorsiva, poiché hai la funzione gcd che chiama la funzione gcd. Solo per martellarlo a casa, eccolo in c # (di nuovo, supponendo che 0 non venga mai passato come parametro):

int gcd(int a, int b)
{   
    if (a % b == 0) //this is a stopping condition
    {
        return b;
    }

    return (gcd(b, a % b)); //the call to gcd here makes this function recursive
}

In un programma, è importante avere una condizione di arresto, altrimenti la funzione si ripeterà per sempre, il che alla fine causerà un overflow dello stack!

La ragione per usare la ricorsione qui, piuttosto che un ciclo while o qualche altro costrutto iterativo, è che mentre leggi il codice ti dice cosa sta facendo e cosa succederà dopo, quindi è più facile capire se funziona correttamente .

0
Matt Ellen