31 окт. 2010 г.

Applicative-order evaluation vs Normal-order evaluation

Нормальным порядком вычисления (normal-order evaluation), если кратко, можно назвать такой порядок вычисления, что сперва происходит «полная подстановка, затем редукция».

Аппликативный порядок вычисления (applicative-order evaluation), напротив, сперва происходит «вычисление аргументов, затем вызов процедуры».

В качестве наглядного примера - попробуем реализовать свою собственную функцию and для clojure.
(defn
#^{:test (fn []
(assert (true? (my-and true)))
(assert (false? (my-and true false)))
(assert (true? (my-and true true)))
(assert (false? (my-and true true false)))
(assert (true? (my-and true true true)))
)}
my-and
([x] x)
([x & more] (if (false? x) x (reduce my-and more)))
)
Отлично.

Почему же тогда автор clojure решил использовать макросы в реализации функции and ?

В группе языков lisp, как впрочем и во многих других языках, используется аппликативный порядок вычисления (в общем случае, если не используются мощности «синтаксического сахара»).

Разница между my-and и and в том, что my-and использует аппликативный порядок (т.е все аргументы будут вычислены до вызова функции), в то время как and использует несколько иной порядок.
И, поэтому, один из легальных способов изменить порядок вычисления - применение макросов.

В качестве примера используем функцию foo:
user=> (defn foo[x] (do (println "foo") x))
user=> (my-and (foo false) (foo false) (foo true))
foo
foo
foo
false
user=> (and (foo false) (foo false) (foo true))
foo
false


P.S. Строго говоря, and не использует нормальный порядок вычисления, а «псевдонормальный порядок» на один шаг.

1 комментарий:

Анонимный комментирует...

Правильно я понимаю, что в Clojure по умолчанию аппликативный порядок?