Один из моих любимых способов проверить мощь изучаемого языка — попытаться реализовать различные комбинаторы с фиксированной точкой. Поскольку я изучаю 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 не работают; и
- Как их исправить?
(*)
равно1
, вам не нужен1
как частный случай в функции!
.(defn ! [n] (apply * (map inc (range n))))
работает, как и(defn ! [n] (reduce * (range 1 (inc n))))
, который я предпочитаю. - person Thumbnail   schedule 19.02.2020