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


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)). Было бы очень полезно, если бы кто-нибудь мог объяснить эту концепцию, используя модель стека.


Если первый член списка не является результатом, выполните поиск в остальной части списка . Это то, что 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.

Спасибо за иллюстрацию, но на самом деле не использовать последний возврат
— user1369975

Без последнего возвращения вы никогда не
— пройдете
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.