【问题标题】:Why is my lazy recursive Clojure function being realized?为什么我的惰性递归 Clojure 函数被实现了?
【发布时间】:2017-08-30 21:33:46
【问题描述】:

我正在尝试解决 4Clojure 问题(序列减少),但我遇到了困难。问题是重新实现reductions函数。

在我看来,这个函数应该返回一个惰性序列,但它不会 - 评估 (take 5 (redux + (range))) 会导致无限循环。

这是我的代码:

(defn redux
  ([f coll] 
      (redux f (first coll) (rest coll)))
  ([f val coll] 
      ((fn red [val coll s]
           (if (empty? coll)
               s
               (lazy-seq
                  (let [val (f val (first coll))]
                    (red val 
                         (rest coll)
                         (conj s val))))))
       val coll [val])))

为什么这个函数没有返回惰性序列?

【问题讨论】:

  • 我没有时间完整回答,但是看看你的代码,问问自己,一个元素实际上是什么时候被添加到某个地方的?你会注意到它不是。这只是一系列调用red 的thunk (lazy-seq) ...如果你有lazy-seq,你的代码中可能应该有cons,一般来说,因为这实际上创建了列表。

标签: clojure lazy-evaluation


【解决方案1】:

代码中有一些误解。 Noisesmith 在#clojurians 聊天中指出了以下几点(以及 Josh 的评论):

  1. 上述函数中没有步骤可以评估列表的头部而不是尾部。
  2. 它会立即进行自递归,并且要获取 n+1 元素,您需要进行递归调用。
  3. lazy-seq 应该始终调用 cons 或一些类似的函数,让您无需重复返回列表的下一项。
  4. conj 从不懒惰,向量从不懒惰。
  5. 你不能在没有意识到整个事情的情况下追加到列表中。

我将代码修改如下:

(fn redux
  ([f coll] 
      (redux f (first coll) (rest coll)))
  ([f val coll] 
      (cons val
        ((fn red [val coll]
           (lazy-seq
             (when-not (empty? coll)
               (let [val (f val (first coll))]
                 (cons val (red val (rest coll)))))))
          val coll))))

注意使用cons 而不是conj

【讨论】:

  • 看起来不错,但你为什么使用内部fn?你可以让redux 递归,就像clojure.core 实现一样。
  • @AlephAleph @Josh 额外的缺点和内部 fn 是在列表开头获得第一个未评估的 val 的快速技巧。我一直盯着这个问题太久了,想出一个更优雅的解决方案 atm。
  • @LincolnBergeson FWIW 更优雅的解决方案看起来像(defn redux [f val coll] (lazy-seq (cons val (... (redux ...)))))
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多