В примере, приведенном в книге, список реализован в императивном стиле.Функция itl
изменяет список, то есть изменяет список, удаляя первый элемент.После вызова itl
первый элемент практически исчезает.
Сложная часть - это порядок, в котором аргументы icons
выполняются в операторе:
else icons (f (ihd l)) (imap f (itl l))
В спецификации OCaml порядок не указан.В прошлый раз, когда я проверял, компилятор INRIA сначала упорядочил последний аргумент, поэтому (imap f (itl l))
выполняется перед (f (ihd l))
.Это означает, что к тому моменту, когда ihd
фактически вызывается, itl
вызывается достаточно раз, чтобы удалить все элементы l
.
В качестве примера, давайте рассмотрим предпоследний рекурсивный вызов- l
это просто ["three"]
.Вы думаете, что это приведет к:
icons (f "three") (imap f [])
Однако давайте посмотрим, что произойдет, если мы сначала вызовем itl
:
(* l = ["three"] *)
let tail = (itl l) in (* tail = [], l = [] *)
let head = (ihd l) in (* l = [], ihd raises an exception *)
icons head (imap f tail)
В качестве примера попробуйте запустить этот код:
let f x y z = x + y + z
f (print_int 1; 1) (print_int 2; 2) (print_int 3; 3)
На моей машине (OCaml 3.12) я получаю этот вывод:
321- : int = 6