Motivo dell'istruzione return nella chiamata di funzione ricorsiva


14

Avevo solo un dubbio nella mia mente. La seguente subroutine (per cercare un elemento, ad esempio in un elenco) ha un'istruzione return alla fine:

list *search_list(list *l, item_type x) {
  if (l == NULL) return(NULL);
  if (l->item == x)
    return(l);
  else
    return( search_list(l->next, x) );
}

Non riesco a ottenere il significato dell'istruzione return alla fine (ovvero return search_list (l-> next, x)). Sarebbe davvero utile se qualcuno potesse spiegare questo concetto, usando il modello di stack.


Se il primo termine dell'elenco non è il risultato, cercare nel resto dell'elenco . Questo è ciò che fa l'ultimo return.
Giorgio,

@ Giorgio, perché non sarebbe bastata solo una chiamata di funzione, perché prima è necessario un ritorno?
user1369975,

7
Perché è necessario restituire il valore restituito dalla funzione
Esailija,

7
Downvoter: si prega di rendersi conto che, a seconda dello sfondo dell'OP, non è affatto ovvio che cosa returnfaccia. In effetti, nei linguaggi funzionali (e in alcuni misti, come Scala) return non è necessario : il valore della funzione ricorsiva è il valore della sua ultima espressione. Scrivere semplicemente search_list(l->next, x)senza returnavrebbe funzionato alla Scala! Il significato returndell'affermazione è ovvio solo per i programmatori con un background imperativo.
Andres F.

OP: il tuo frammento di codice è scritto in C?
Andres F.

Risposte:


19

Un'istruzione return restituisce un valore al chiamante immediato del frame di chiamata della funzione corrente. In caso di ricorsione, questo chiamante immediato può essere un'altra invocazione della stessa funzione.

Nella maggior parte delle lingue, se non si utilizza il valore restituito di una funzione chiamata (ricorsivamente o meno), il valore restituito viene scartato oppure si tratta di un errore diagnosticabile. Esistono alcune lingue in cui il valore di ritorno dell'ultima chiamata di funzione viene riutilizzato automaticamente come valore di ritorno dell'attuale chiamata di funzione, ma non fanno distinzioni tra chiamate di funzione normali e ricorsive.

Supponendo che i valori restituiti inutilizzati vengano scartati silenziosamente, se si fosse scritto il codice in questo modo:

list *search_list(list *l, item_type x) {
  if (l == NULL) return(NULL);
  if (l->item == x)
    return(l);
  else
    search_list(l->next, x); // no return!
}

search_listrestituirà quindi solo un valore definito per un elenco vuoto (NULL) o se il primo elemento corrisponde al valore che stai cercando. Non appena la funzione passa alla chiamata ricorsiva, non si sa quale sarà il risultato, poiché il risultato della chiamata ricorsiva viene scartato.

Inoltre, prometti di restituire un valore dalla tua funzione, ma hai un percorso (quello ricorsivo) in cui non specifichi quale valore restituire. A seconda della lingua che usi, questo di solito si traduce in una diagnostica obbligatoria o in un comportamento indefinito (che è una scorciatoia per: tutto può succedere e può cambiare in qualsiasi momento senza preavviso. Non ritenere nessuno se non responsabile se si rovina la tua presentazione più importante). Ci sono alcune situazioni in cui il valore di ritorno mancante potrebbe sembrare funzionare, ma ciò potrebbe cambiare alla successiva esecuzione del programma (con o senza ricompilazione).


FWIW, Perl restituisce automaticamente il risultato dell'ultima espressione, il che penso significhi che riutilizzerebbe il valore restituito. Ma non lo tocco da anni, quindi non ne sono certo.
Bobson,

1

Due cose; Restituire l'intero elenco nel caso in cui trovi la "x" che stai cercando non garantisce necessariamente l'utilizzo della ricorsione, ma a parte questo, considera quanto segue:

Supponiamo che tu stia cercando un valore di X = "dicembre" e che il tuo elenco sia il valore numerico dei mesi dell'anno, un puntatore al mese successivo e che le voci l-> dell'elenco siano i nomi espliciti del mesi. (Gennaio, febbraio, ..., dicembre). Sono necessari i tre ritorni per i possibili risultati. Il primo, return (NULL) è necessario se l'elenco non contiene la X che stai cercando. Il secondo, (return (l)) restituisce la lista, in questo caso, per farti sapere che hai trovato la tua "x". L'ultimo è dove entra in gioco il modello di stack. Le chiamate successive alla funzione avrebbero aggiornato le variabili locali (in particolare, l-> item) in questo modo:

1: l->item = January
   returns search_list(l->next, x)
2: l->item = February
   returns search_list(l->next, x)
3-11: March, April, May, June, July, August, September, October, November
   all return search_list(l->next, x)
12: l->item = December
  This matches the second if() and returns your list, letting you know you found your search item.

, grazie per la tua illustrazione, ma in realtà non
approfittare

Senza l'ultimo ritorno, non
supererai
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.