Поскольку это означает, рекурсия хвоста не применяется. Но если вы посмотрите на конец second answer на вопрос, с которым вы связались, вы можете увидеть, как правильно переписать функцию. Начиная с
unsigned int f(unsigned int a) {
if (a == 0) {
return a;
}
return f(a-1) + f(a-1);
}
переписывают его следующим образом:
unsigned int f(unsigned int a) {
if (a == 0) {
return a;
}
return 2 * f(a-1);
}
Даже сейчас, хвостовая рекурсия еще не имеет прямого применения. Нам нужно убедиться, что возврат строго соответствует форме return f(....)
. Перепишите эту функцию еще раз:
unsigned int f(unsigned int a, unsigned int multiplicative_accumulator = 1) {
if (a == 0) {
return multiplicative_accumulator * a;
}
return f(a-1, multiplicative_accumulator * 2);
}
Теперь применяется рекурсия хвоста. Это использует значение по умолчанию для multipicative_accumulator (спасибо @Pubby), чтобы первый вызов f
мог быть просто f(x)
, иначе вам нужно было бы написать что-то f(x,1)
.
Пару финальных нот благодаря @SteveJessop:
- Это было безопасно изменить
f(a+1)+f(a+1)
к 2*f(a+1)
, поскольку F не имеет побочных эффектов (печать, изменение кучу, что такие вещи). Если у f были побочные эффекты, эта перепись недействительна.
- Оригинал был эквивалентен
(2*(2*(2*a))
(или, точнее, (((a+a)+(a+a))+((a+a)+(a+a)))
), тогда как текущая версия больше похожа на (((2*2)*2)*a)
. Это прекрасно, особенно для ints, потому что умножение является ассоциативным и дистрибутивным. Но это не будет точным эквивалентом для float
, где вы, вероятно, получите небольшие округлые расхождения. С арифметикой с плавающей запятой иногда a*b*c
может немного отличаться от c*b*a
.
Почему вы не используете значение по умолчанию? 'unsigned int f (unsigned int a, unsigned int multipicative_accumulator = 1)' – Pubby
Спасибо @Pubby. Я отредактировал свой ответ. –
Обратите внимание, что этот код эквивалентен, потому что (a) 'f' не имеет побочных эффектов, поэтому количество раз, когда функция вызывается, не имеет значения, и (b) умножение в' unsigned int' является ассоциативным и дистрибутивным через дополнение. Умножение в 'float' (например) не является ассоциативным, поэтому это преобразование не приведет к эквивалентному коду. Часто вам не важно, какая разница, но вы должны быть немного осторожны с такими вещами. –