Что такое устранение хвостовой рекурсии? - PullRequest
32 голосов
/ 06 августа 2009

Стив Йегге упомянул об этом в сообщении в блоге , и я понятия не имею, что это значит, может кто-нибудь заполнить меня?

Это то же самое, что и оптимизация хвостового вызова ?

Ответы [ 2 ]

52 голосов
/ 06 августа 2009

Исключение хвостовых вызовов - это оптимизация, которая экономит место в стеке. Он заменяет функцию call на goto . Исключение хвостовой рекурсии - это то же самое, но с добавленным ограничением, которое функция вызывает сама.

По сути, если самое последнее, что делает функция A, это return A(params...), то вы можете устранить выделение стекового фрейма и вместо этого установить соответствующие регистры и перейти непосредственно в тело функции.

Рассмотрим (мнимое) соглашение о вызовах, которое передает все параметры в стеке и возвращает значение в некотором регистре.

Некоторые функции могут компилироваться до (на воображаемом языке ассемблера):

function:
//Reading params B, C, & D off the stack
pop B
pop C
pop D
//Do something meaningful, including a base case return
...
//Pass new values for B, C, & D to a new invocation of function on the stack
push D*
push C*
push B*
call function
ret

Независимо от того, что на самом деле делает вышеперечисленное, для каждого вызова функции требуется целый новый кадр стека. Однако, поскольку после хвостового вызова функции ничего не происходит, кроме возврата, мы можем безопасно оптимизировать этот случай.

В результате:

function:
//Reading params B, C, & D off the stack (but only on the first call)
pop B
pop C
pop D
function_tail_optimized:
//Do something meaningful, including a base case return
...
//Instead of a new stack frame, load the new values directly into the registers
load B, B*
load C, C*
load D, D*
//Don't call, instead jump directly back into the function
jump function_tail_optimized

Конечный результат - эквивалентная функция, которая экономит много места в стеке, особенно для входных данных, которые приводят к большому количеству рекурсивных вызовов.

В моем ответе требуется много воображения, но я думаю, что вы можете понять эту идею.

8 голосов
/ 06 августа 2009

от здесь :

"... Устранение хвостовой рекурсии особый случай устранения хвостовых вызовов в котором хвостовой вызов является вызовом сама функция. В этом случае вызов может быть заменен прыжком на начало функции после перемещения новые аргументы в соответствующие регистры или ячейки стека ... "

из Википедия :

"... Когда вызывается функция, компьютер должен" запомнить "место, из которого она была вызвана, адрес возврата, чтобы он мог вернуться в это место с результатом после завершения вызова. Как правило, это информация сохраняется в стеке, простом списке мест возврата в порядке времени, когда были достигнуты места вызова, которые они описывают. Иногда последнее, что делает функция после выполнения всех других операций, - это просто вызывает функцию, возможно, саму себя и возвращает свой результат. С помощью хвостовой рекурсии нет необходимости запоминать место, из которого мы вызываем - вместо этого мы можем оставить стек в покое, и вновь вызванная функция вернет свой результат непосредственно исходному вызывающему объекту. Преобразование вызова в ответвление или переход в таком случае называется оптимизацией хвостового вызова. Обратите внимание, что хвостовой вызов не должен появляться лексически после всех других операторов в исходном коде; важно только, чтобы его результат быть немедленно возвращен, так как Функция lling никогда не получит возможности что-либо сделать после вызова, если будет выполнена оптимизация .... "

...