Причина оператора возврата в рекурсивном вызове функции

14

Я просто сомневался. Следующая подпрограмма (например, для поиска элемента в списке) имеет оператор return в конце:

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) );
}

Я не могу получить значение оператора return в конце (т. Е. Return search_list (l-> next, x)). Было бы очень полезно, если бы кто-нибудь мог объяснить эту концепцию, используя модель стека.

user1369975
источник
Если первый член списка не является результатом, выполните поиск в остальной части списка . Это то, что returnделает последний .
Джорджио
@ Джорджио, Почему бы не хватить только вызова функции, зачем нужен возврат до этого?
user1369975
7
Потому что вам нужно вернуть значение, которое возвращает функция
Esailija
7
Downvoters: пожалуйста, поймите, что, в зависимости от фона ОП, совсем не очевидно, что returnделает. На самом деле, в функциональных языках (и некоторых смешанных, таких как Scala) return нет необходимости : значение рекурсивной функции - это значение ее последнего выражения. Простое написание search_list(l->next, x)не returnсработало бы в Scala! Смысл returnзаявления очевиден только для программистов с императивным фоном.
Андрес Ф.
OP: ваш фрагмент кода написан на C?
Андрес Ф.

Ответы:

19

Оператор return передает значение обратно непосредственному вызывающему кадру call-кадра текущей функции. В случае рекурсии этот непосредственный вызывающий объект может быть еще одним вызовом той же функции.

В большинстве языков, если вы не используете возвращаемое значение вызванной вами функции (рекурсивно или нет), либо возвращаемое значение отбрасывается, либо это диагностируемая ошибка. Есть некоторые языки, в которых возвращаемое значение последнего вызова функции автоматически повторно используется как возвращаемое значение текущего вызова функции, но они не различают обычные и рекурсивные вызовы функций.

Предполагая, что неиспользуемые возвращаемые значения молча отбрасываются, если вы написали такой код:

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_listвозвращает только определенное значение для пустого списка (NULL) или если первый элемент соответствует значению, которое вы ищете. Как только функция переходит в рекурсивный вызов, вы не знаете, каким будет результат, потому что результат рекурсивного вызова отбрасывается.

Кроме того, вы обещаете вернуть значение из вашей функции, но у вас есть путь (рекурсивный), в котором вы не указываете, какое значение возвращать. В зависимости от языка, который вы используете, это обычно приводит либо к обязательной диагностике, либо к неопределенному поведению (что означает сокращение: может произойти все, что угодно, и может измениться в любое время без уведомления. ваша самая важная презентация). В некоторых ситуациях пропущенное возвращаемое значение может работать, но оно может измениться при следующем запуске программы (с перекомпиляцией или без нее).

Барт ван Инген Шенау
источник
FWIW, Perl возвращает результат последнего выражения автоматически, что, я думаю, означает, что оно будет повторно использовать возвращаемое значение. Но я не касался этого годами, поэтому я не уверен в этом.
Бобсон
1

Две вещи; Возвращение всего списка в том случае, если вы нашли искомую букву «х», не обязательно гарантирует использование рекурсии, но, кроме этого, рассмотрите следующее:

Предположим, вы ищете значение X = «Декабрь», и ваш список представляет собой числовое значение месяцев года, указатель на следующий месяц, а элементы l-> в списке - это прописанные имена месяцы. (Январь, февраль, ..., декабрь). Вам нужны три возврата для возможных результатов. Первый, return (NULL) необходим, если в списке нет нужного вам X. Второй (return (l)) возвращает список, в данном случае, давая вам знать, что вы нашли свой «x». Последнее, где модель стека вступает в игру. Последовательные вызовы функции обновляли бы локальные переменные (в частности, l-> item) следующим образом:

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.
panhandel
источник
Спасибо за иллюстрацию, но на самом деле не использовать последний возврат
user1369975
Без последнего возвращения вы никогда не
пройдете