В Scheme, первом языке, который приходит мне на ум, когда я думаю о хвостовых вызовах, второй случай гарантированно будет хвостовым вызовом согласно спецификации языка. (Терминологическое примечание: предпочтительно называть такие вызовы функций «хвостовыми вызовами».)
Спецификация Scheme точно определяет, какие хвостовые вызовы есть в Scheme, и указывает, что компиляторы поддерживают их специально. Вы можете увидеть определение в 11.20. Хвостовые вызовы и хвостовые контексты из R 6 RS ( source ).
Обратите внимание, что в Схеме спецификация ничего не говорит об оптимизации хвостовых вызовов. Скорее, это говорит о том, что реализация должна поддерживать неограниченное количество активных оконечных вызовов - семантическое свойство времени выполнения языка. Они могут быть реализованы как обычные вызовы, но обычно это не так.
Пример, в C:
Возьмите C-версию вашего примера.
int example(int arg)
{
if (arg == 0)
return 0;
if ((arg % 2) == 0)
return example(arg - 1);
return 3 + example(arg - 1);
}
Скомпилируйте его, используя обычные настройки оптимизации gcc (-O2
) для i386:
_example:
pushl %ebp
xorl %eax, %eax
movl %esp, %ebp
movl 8(%ebp), %edx
testl %edx, %edx
jne L5
jmp L15
.align 4,0x90
L14:
decl %edx
testl %edx, %edx
je L7
L5:
testb $1, %dl
je L14
decl %edx
addl $3, %eax
testl %edx, %edx
jne L5
L7:
leave
ret
L15:
leave
xorl %eax, %eax
ret
Обратите внимание, что в коде сборки нет вызовов функций. GCC не только оптимизировал ваш хвостовой вызов в прыжке, но и оптимизировал не хвостовой вызов в прыжке.