Почему один фолд-хвост рекурсивный, а другой нет? - PullRequest
0 голосов
/ 09 октября 2018
fun fold1 f acc lst =
    case lst of
         [] => acc
       | hd::tl => fold1 f (f (acc,hd)) tl

fun fold2 f acc lst =
    case lst of
         [] => acc
       | hd::tl => f (fold2 f acc tl, hd)

Почему первый хвост рекурсивен, а другой нет?

Я думаю, что оба хвост рекурсивны.

Ответы [ 3 ]

0 голосов
/ 09 октября 2018

Почему первый хвост рекурсивен, а другой нет?

С учетом определения хвост-рекурсив ,

Вызов функции называется хвостовой рекурсией, если после возврата функции ничего не делать, кроме как возвращать ее значение.

В первой функции

fun fold1 f acc lst =
    case lst of
         [] => acc
       | hd::tl => fold1 f (f (acc,hd)) tl

все остальные вычисления (f (acc, hd)) выполняются в качестве аргумента для fold1, что означает , после того как функция вернется, ничего не нужно делать, кроме как вернуть ее значение .

Inвторая функция,

fun fold2 f acc lst =
    case lst of
         [] => acc
       | hd::tl => f (fold2 f acc tl, hd)

, все остальные вычисления (f (..., hd)) выполняются после выполнения fold2 f acc tl, что означает там означает что-то дляdo после того, как функция вернет , а именно compute f (..., hd).

Хвосто-рекурсивные функции имеют определяющую характеристику, заключающуюся в том, что самое внешнее выражение тела рекурсивной функции - это вызов самого себя.Если что-то еще вычисляется в функции, это должно произойти за до этого вызова, например, в качестве аргумента функции или в привязке let.

Например, следующий рефакторингfold1 также хвост-рекурсивен:

fun fold1 f acc0 [] = acc0
  | fold1 f acc0 (x::xs) =
    let val acc1 = f (acc0, x)
    in fold1 f acc1 xs
    end

и следующий рефакторинг fold2 не является:

fun fold2 f acc0 [] = acc0
  | fold2 f acc0 (x::xs) =
    let val acc1 = fold2 f acc0 xs
    in f (acc1, x)
    end

Поскольку после fold1 f acc1 xs больше ничего не нужно делатьконтекст вызова функции (стековый фрейм) можно безопасно отбросить.Более простой пример нерекурсивной и хвостовой рекурсии:

fun length [] = 0
  | length (_::xs) = 1 + length xs

fun length xs =
    let fun go [] count = count
          | go (_::ys) count = go ys (1 + count)
    in go xs 0
    end
0 голосов
/ 10 октября 2018

TL; DR

Если это хвостовая рекурсия, она должна располагаться в хвостовой позиции.Аргументы не являются хвостовой позицией.


В вашем случае:

case lst of
     [] => e1
   | hd::tl => e2

e1 и e2 оба являются хвостовым регистром, потому что нет других выражений после оценки значения двух выражений (IOW, они располагаются в хвостовой позиции).Вот почему fold1 является хвостовой рекурсией.

Что касается f (fold2 f acc tl, hd), f(...) действительно находится в хвостовой позиции.Но fold2 здесь не в хвостовой позиции, потому что после оценки его значения вам все равно нужно вызвать f.Так что f - это хвостовая рекурсия, а fold2 - нет.

0 голосов
/ 09 октября 2018

Первый является хвосто-рекурсивным, потому что рекурсивный вызов (до fold1) появляется в конце тела функции (он формирует "хвост"):

fold1 f (f (acc,hd)) tl

вызывает f (acc,hd)сначала, затем передает результат (вместе с f и tl) в fold1.Это последнее, что делает функция;результат рекурсивного вызова fold1 передается нашему вызывающему.


Второй не является хвостово-рекурсивным, поскольку рекурсивный вызов (до foldl2) - это не последнее, что функциявыполняет:

f (fold2 f acc tl, hd)

сначала вызывает fold2 f acc tl, затем создает кортеж из результата и hd, затем передает этот кортеж f.

Есть две вещи, которые происходятпосле рекурсивного вызова fold2: построение кортежа ((..., hd)) и другой вызов функции (f ...).В частности, результат вызова fold2 не передается напрямую нашему абоненту.Вот почему этот код не является хвост-рекурсивным.

...