Один из моих любимых способов проверить силу языка, который я изучаю, - это попытаться реализовать различные комбинаторы с фиксированной запятой. Так как я изучаю Clojure (хотя я не новичок в lisps), я сделал то же самое для него.
Сначала немного "тестируемого" кода, факториал:
(def !'
"un-fixed factorial function"
(fn [f]
(fn [n]
(if (zero? n)
1
(* n (f (dec n)))))))
(defn !
"factorial"
[n]
(if (zero? n)
1
(apply * (map inc (range n)))))
Для любого комбинатора c
, который я реализую, я хочу убедиться, что ((c !') n)
равен (! n)
.
Мы начнем с традиционного Y:
(defn Y
"pure lazy Y combinator => stack overflow"
[f]
(let [A (fn [x] (f (x x)))]
(A A)))
Но, конечно, Clojure не настолько ленив, как этот, поэтому мы поворачиваемся к Z:
(defn Z
"strict fixed-point combinator"
[f]
(let [A (fn [x] (f (fn [v] ((x x) v))))]
(A A)))
И действительно, он утверждает, что (= ((Z !') n) (! n))
.
Теперь приходит моя проблема: я не могу получить ни одного из U или комбинатор Тьюринга (тэта-v) для правильной работы. Я подозреваю, что с U это ограничение по языку, в то время как с theta-v я более склонен полагать, что это неверное прочтение нотации Википедии :
(defn U
"the U combinator => broken???"
[f]
(f f))
(defn theta-v
"Turing fixed-point combinator by-value"
[f]
(let [A (fn [x] (fn [y] (y (fn [z] ((x x) y) z))))]
(A A)))
Пример опыта REPL:
((U !') 5)
;=> Execution error (ClassCastException) at fix/!'$fn (fix.clj:55).
;=> fix$_BANG__SINGLEQUOTE_$fn__180 cannot be cast to java.lang.Number
((theta-v !') 5)
;=> Execution error (ClassCastException) at fix/theta-v$A$fn (fix.clj:36).
;=> java.lang.Long cannot be cast to clojure.lang.IFn
Может кто-нибудь объяснить
- Почему эти реализации U и theta-v не работают; и
- как их исправить?