【问题标题】:Simplest Lazy function in ClojureClojure 中最简单的惰性函数
【发布时间】:2012-02-20 13:43:27
【问题描述】:

我很难理解懒惰。

谁能帮我理解为什么我下面的函数不是懒惰的

(defn my-red
    ([f coll] (my-red f (first coll) (rest coll) ))
    ([f init coll] 
      (let [mr (fn [g i c d] 
            (if (empty? c) d 
        (recur  g (g  i (first c)) (rest c)  (conj d (g  i (first c)) ))))] 
    (lazy-seq (mr f init coll [])))))

而 clojure.org/lazy 上给出的这个例子是

(defn filter
  "Returns a lazy sequence of the items in coll for which
  (pred item) returns true. pred must be free of side-effects."
  [pred coll]
  (let [step (fn [p c]
                 (when-let [s (seq c)]
                   (if (p (first s))
                     (cons (first s) (filter p (rest s)))
                     (recur p (rest s)))))]
    (lazy-seq (step pred coll))))

【问题讨论】:

    标签: clojure lazy-evaluation


    【解决方案1】:

    filter 的惰性来自于递归循环的if 分支中对filter 的调用。这就是代码遇到另一个 lazy-seq 并停止评估 seq 直到请求另一个元素的地方。

    您对my-red 的实现仅对lazy-seq 进行了一次调用,这意味着您的序列要么根本未评估,要么完全评估。

    【讨论】:

      【解决方案2】:

      mr 函数只会在整个 coll 中重复出现。也许你的缩进误导了你。正确缩进,并删除了一些无用的参数,函数看起来像这样:

      (defn my-red
        ([f coll] (my-red f (first coll) (rest coll) ))
        ([f init coll] 
           (let [mr (fn [i c d] 
                      (if (empty? c)
                        d 
                        (recur (f i (first c))
                               (rest c)
                               (conj d (f i (first c))))))] 
             (lazy-seq (mr init coll [])))))
      

      基本上,您将 (lazy-seq) 包装在 mr 函数周围,该函数在一个大的递归循环中完成所有工作。

      【讨论】:

        【解决方案3】:

        lazy-seq 所做的只是接受它的参数并延迟它的执行。为了产生真正的惰性序列,每个链接都必须包装在惰性序列调用中。懒惰的“粒度”是在调用lazy-seq 之间完成了多少工作。解决这个问题的一种方法是使用返回惰性序列的更高级别的函数。

        另外,尾递归和惰性是相互排斥的。这不会导致堆栈溢出,因为在每一步,递归调用都被包装到一个惰性序列中并返回。如果调用者然后尝试评估惰性序列,则调用递归调用,但它是由序列函数的原始调用者调用的,而不是序列函数本身,这意味着堆栈不会增长。这有点类似于通过蹦床实现优化尾递归的想法(参见 Clojure 的 trampoline 函数)。

        这是一个懒惰的版本:

        (defn my-red
          ([f coll] (my-red f (first coll) (rest coll) ))
          ([f init coll] 
           (let [mr (fn mr [g i c] 
                      (if (empty? c)
                        nil
                        (let [gi (g  i (first c))]
                          (lazy-seq (cons gi (mr g gi (rest c)))))))] 
             (lazy-seq (mr f init coll)))))
        

        注意mr 的每次运行如何立即返回 nil 或惰性序列,并且递归调用来自 lazy-seq 调用。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-12-08
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多