Редактировать: я нашел частичный ответ на свой вопрос в процессе написания этого, но я думаю, что его можно легко улучшить, поэтому я все равно опубликую его. Может быть, есть лучшее решение?
Я ищу простой способ определить рекурсивные функции в форме let
, не прибегая к letfn
. Это, вероятно, необоснованный запрос, но причина, по которой я ищу эту технику, заключается в том, что у меня есть смесь данных и рекурсивных функций, которые зависят друг от друга таким образом, что требуется много вложенных операторов let
и letfn
.
Я хотел написать рекурсивные функции, которые генерируют такие ленивые последовательности (на примере последовательности Фибоначчи):
(let [fibs (lazy-cat [0 1] (map + fibs (rest fibs)))]
(take 10 fibs))
Но в clojure кажется, что fibs
не может использовать свой собственный символ во время привязки. Очевидный способ обойти это - использовать letfn
(letfn [(fibo [] (lazy-cat [0 1] (map + (fibo) (rest (fibo)))))]
(take 10 (fibo)))
Но, как я уже говорил ранее, это приводит к громоздкой вложенности и чередованию let
и letfn
.
Чтобы сделать это без letfn
и используя только let
, я начал с написания чего-то, что использует то, что я считаю U-комбинатором (только сегодня услышал об этой концепции):
(let [fibs (fn [fi] (lazy-cat [0 1] (map + (fi fi) (rest (fi fi)))))]
(take 10 (fibs fibs)))
Но как избавиться от избыточности (fi fi)
?
Именно в этот момент я обнаружил ответ на свой вопрос после часа борьбы и постепенного добавления битов в комбинатор Q.
(let [Q (fn [r] ((fn [f] (f f)) (fn [y] (r (fn [] (y y))))))
fibs (Q (fn [fi] (lazy-cat [0 1] (map + (fi) (rest (fi))))))]
(take 10 fibs))
Как называется этот комбинатор Q
, который я использую для определения рекурсивной последовательности? Похоже на комбинатор Y без аргументов x
. Это то же самое?
(defn Y [r]
((fn [f] (f f))
(fn [y] (r (fn [x] ((y y) x))))))
Есть ли в clojure.core или clojure.contrib другая функция, обеспечивающая функциональность Y или Q? Я не могу представить, что то, что я только что сделал, было идиоматичным...