在Clojure的列表中,惯用和懒惰最终真实

时间:2016-05-20 08:10:40

标签: clojure lazy-evaluation

我希望有一个函数/宏来检查列表最终具有真值,我希望评估是懒惰的。这是我没有懒惰评估的说明性实现:

(defn eventual [cols]
  (or (first cols) (if-let [rs (rest cols)]
                     (eventual rs))
                     false))

这是一个简单的例子来说明:

(if (eventual [false (+ 1 2) (* 10000 10000)])
  true
  false)

我觉得懒惰的评价必然会有一个含义。也许我现在只是蒙蔽双眼。请帮忙。感谢

2 个答案:

答案 0 :(得分:2)

您可以检查序列是否包含至少一个带有some函数的真实元素:

(some identity col)

如果你将一个懒惰的序列传递给col,它会将其内容评估为第一个真实元素,并且不会实现其余部分:

(let [col (take
            10
            (iterate
              #(do (println "Generating new value from " %) (inc %))
              0))]
  (some #(> % 5) col))

产生

Generating new value from  0
Generating new value from  1
Generating new value from  2
Generating new value from  3
Generating new value from  4
Generating new value from  5
true

如您所见,值6..9根本不会产生。

您还应该仔细检查传递给col的{​​{1}}是否真的很懒,而且还没有实现,因为它可能会让您感到困惑。

答案 1 :(得分:2)

您的eventual功能尽可能地懒惰。它急切地搜索第一个真实项目然后停止。但它有问题:

  • 无法在空集合上终止。 (rest ())(), 这是真的。使用next代替rest(next ())nil, 这是假的。
  • 这是真正的递归。它会在很长时间内吹掉堆栈 搜索。试试(eventual (repeat false))。因为递归是 尾递归,您可以在其位置使用recur来解决此问题。
  • 虽然我们处于此状态,但返回nil而非false是不恰当的, 一个集合用完了。所以放弃最后的false

我们最终

(defn eventual [cols]
  (or (first cols) (if-let [rs (next cols)]
                     (recur rs))))

如果cols为空,我会感到有些不安。基于the source for some的代码更清晰:

(defn eventual [coll]
    (when (seq coll)
      (or (first coll) (recur next coll))))

但使用(some identity col)Piotrek建议,可能是最好的。