在Clojure中打结:没有(明确的,丑陋的)突变的循环引用?

时间:2012-07-19 19:06:12

标签: recursion clojure lazy-evaluation

Clojure For Comprehension example的答案中,我有一个处理自己输出的函数:

(defn stream [seed]
  (defn helper [slow]
    (concat (map #(str (first slow) %) seed) (lazy-seq (helper (rest slow)))))
  (declare delayed)
  (let [slow (cons "" (lazy-seq delayed))]
    (def delayed (helper slow))
    delayed))

(take 25 (stream ["a" "b" "c"]))
("a" "b" "c" "aa" "ab" "ac" "ba" "bb" "bc" "ca" "cb" "cc" "aaa" "aab" "aac" 
"aba" "abb" "abc" "aca" "acb" "acc" "baa" "bab" "bac" "bba")

它的工作原理是创建一个前向引用(delayed),它用作延迟序列(slow)中的第二个条目。该序列被传递给该函数,该函数是惰性的,并且该函数的输出(惰性序列的第一部分,其不需要评估delayed)然后用于设置{的值。 {1}}。

通过这种方式我“打结”。但这在Haskell中更优雅(例如Explanation of “tying the knot”)。鉴于Clojure有delayeddelay,我想知道是否有更好的方法来完成上述工作?

那么问题:在上面的代码中可以以某种方式避免(丑陋的,明确的)突变(force)吗?显然(?)你仍然需要变异,但它可以被“懒惰”构造隐藏吗?

[当我还在努力了解如何做到这一点时,我昨晚有一个类似标题的问题;在上面的代码工作之前没有人回复,所以我删除了它,但我对这种方法并不满意,所以我再试一次。]

另见:Must Clojure circular data structures involve constructs like ref?(有点令人沮丧的是人们在复制问题)。

1 个答案:

答案 0 :(得分:2)

我不确定我是否可以回答一般情况的问题,但这个功能似乎解决了特定情况。

(defn stream
  [seed]
  (let [step (fn [prev] (for [p prev s seed] (str p s)))]
    (for [x (iterate step seed) y x] y)))

虽然我遇到了大(dorun (take ...))的内存不足异常。所以这个功能可能存在问题。