Как есть, рекурсия хвоста не применяется.Но если вы посмотрите в конце второго ответа на вопрос, с которым вы связаны, вы увидите, как правильно переписать функцию.Начиная с
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 );
}
Теперь применима хвостовая рекурсия.При этом используется значение по умолчанию для multiplicative_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)
.Это хорошо, особенно для целых, потому что умножение является ассоциативным и распределительным.Но это не будет точным эквивалентом для float
, где вы, вероятно, получите небольшие расхождения при округлении.С арифметикой с плавающей точкой иногда a*b*c
может немного отличаться от c*b*a
.