Хвостовая рекурсия имеет смысл в (GHC) Haskell, если ваш аккумулятор строг.Чтобы продемонстрировать проблему, вот «след» вашего хвостово-рекурсивного определения fac
:
fac 4
~> facRec 4 1
~> facRec 3 (1*4)
~> facRec 2 ((1*4)*3)
~> facRec 1 (((1*4)*3)*2)
~> facRec 0 ((((1*4)*3)*2)*1)
~> (((1*4)*3)*2) * 1
~> ((1*4)*3) * 2
~> (1*4) * 3
~> 1*4
~> 4 * 3
~> 12 * 2
~> 24 * 1
~> 24
Уровень отступа (приблизительно) соответствует уровню стека.Обратите внимание, что аккумулятор оценивается только в самом конце, и это может вызвать переполнение стека.Хитрость, конечно, состоит в том, чтобы сделать аккумулятор строгим.Теоретически возможно показать, что facRec является строгим, если он вызывается в строгом контексте, но я не знаю ни одного компилятора, который делает это, ATM.GHC выполняет оптимизацию хвостовых вызовов, поэтому для вызовов facRec
используется постоянное пространство стека.
По той же причине foldl'
обычно предпочтительнее foldl
, поскольку перваястрог в аккумуляторе.
Что касается вашей второй части, runhaskell
/ runghc
- это просто оболочка над GHCi.Если GHCi находит скомпилированный код, он будет использовать его, в противном случае он будет использовать интерпретатор байт-кода, который выполняет несколько оптимизаций, поэтому не ожидайте, что произойдут какие-либо необычные оптимизации.