#recursion #overloading
#рекурсия #перегрузка
Вопрос:
Я занимался проблемами рекурсии в своем классе структур данных и алгоритмов, и для некоторых хвостовых рекурсивных функций я не мог думать ни о чем, кроме как о том, чтобы основная функция вызывала вспомогательную функцию, а вспомогательная функция была хвостовой рекурсивной частью, поскольку мне приходилось передавать параметры, отличные от исходной функции.
Хотя это технически хвостовая рекурсия, исходная функция — это та, которая в конечном итоге обрабатывает базовые случаи, а в основном случае передает дополнительные параметры фактической хвостовой рекурсивной функции.
Очевидно, что такой подход противоречит идее рекурсии, особенно в классе для домашних заданий, экзаменов и тому подобного.
Вот почему сегодня я подумал, что вместо создания вспомогательной функции, не мог бы я создать перегруженную версию функции, которая принимает эти дополнительные параметры и продолжает работу? Технически это было бы рекурсивно, поскольку все вызовы в функции относятся к самой себе, хотя и с большим количеством параметров, чем при первоначальном вызове.
Вот примерный пример:
int fibonacci(int n) {
if (n == 1)
return 1;
else
return fibonacci(n, 2, 1, 2);
}
int fibonacci(int n, int f1, int f2, int c) {
if (c == n)
return f1;
else
return fibonacci(n, f1 f2, f1, c 1);
}
Это все еще рекурсия в смысле определения? Будет ли это работать? Будут ли компиляторы, выполняющие оптимизацию рекурсии (не знаю, как это точно работает, но знаю, что оно существует), применять это к этому?
Я предполагаю, что это все еще рекурсия, поскольку технически она все еще вызывает себя, и я бы подумал, что это оптимизирует, просто игнорируя начальный вызов. Но именно поэтому я спрашиваю, чтобы посмотреть, прав я или нет.
Комментарии:
1. Кроме того, вы допустили ошибку, которая очень распространена среди людей, пишущих рекурсивные функции: вы забыли
return
значение вызова функции (напримерreturn fibonacci(...)
).2. Просто исправил это, что странно, поскольку программа все еще выполнялась и выводила ответ, когда я выполнял cout << fibonacci(n)
3. да, это, вероятно, потому, что последний вызов функции возвращает
f1
fibonacci(int, int, int, int)
искомое число Фибоначчи (поэтому оно хранится в регистре eax, если вы используете x86), иreturn
s должен продвигать это вверх, но они этого не делают, но посколькуreturn
s нет, значение последнего вызова функции равноне перезаписывается, так что это просто работает. Но это не всегда будет работать на всех архитектурах, и это технически недопустимо C , потому что «не все пути возвращают значение», о котором ваш компилятор должен вас предупредить.4. Это «стандартный» способ рекурсивной обработки Фибоначчи. Перегрузка не имеет значения, поскольку имена могут быть полностью случайными и при этом иметь желаемую семантику (даже если это будет считаться «плохим кодом») — в статически типизированном языке с перегрузкой сигнатура типа является частью «имени». Хотя функция «prep» сама по себе не является рекурсивной, вторая функция явно является — и именно в этой функции фактически выполняется работа.
5. Еще одно замечание: функция main / original / prep не обрабатывает базовый вариант. Базовый случай — это когда рекурсия заканчивается (в вашем примере
(c==n)
) и обрабатывается вспомогательной функцией. Вы можете удалить тест дляn==1
из основной функции и простоreturn fibonacci(n, 1, 1, 1)
.
Ответ №1:
Да, я бы все равно назвал это «рекурсией». Перегрузка с одним аргументом fibonacci
не является рекурсивной, но перегрузка с 4 аргументами, без сомнения, является рекурсивной.
Тем не менее: не обманывайте себя, думая, что fibonacci
функция в целом является рекурсивной. Перегрузка с одним аргументом никогда не вызывает себя. В Java-подобных языках, где функция / метод однозначно определяется его сигнатурой, у вас все равно есть две разные функции.
Комментарии:
1. Я знаю, я просто всегда чувствую себя глупо, когда пишу многопараметрическую функцию и пишу: «Предположим, что эти переменные — это, это и то, а n — запрошенное число Фибоначчи».
2. @Portaljacker не забывайте об аргументах по умолчанию (если вы используете C ).
3. Подождите, что? Не знал об этом! Какой синтаксис? Я предполагаю, что вы можете заменить эти значения в последовательных рекурсивных вызовах, верно? Какие другие языки поддерживают это? Большая часть моих классных работ написана на Java, но некоторые — на C , так что полезно знать такие вещи!
4. @Portaljacker ваша сигнатура функции будет выглядеть
fibonacci(int n, int f1 = 2, int f2 = 1, int c = 2)
следующим образом . Таким образом, вы можете вызвать его с одним или несколькими (но менее 4) аргументами, а остальные будут иметь свои значения по умолчанию, или вы можете вызвать его со всеми из них, и они будут иметь эти значения.5. Большое вам спасибо! Мне кажется, что я это где-то видел, но я никогда не был уверен, выдумал я это или нет. Есть идеи о других языках, которые это делают?