【问题标题】:Clojure infinite lazy sequences, out of boundsClojure 无限惰性序列,超出范围
【发布时间】:2013-01-11 17:19:36
【问题描述】:

我最近才开始学习 Clojure,如果这有点初级,敬请见谅:

谁能给我解释一下:

=> (def a (lazy-cat
            [0]
            (map inc a)
   ))

=> (take 5 a)
(0 1 2 3 4)

=> (def b (lazy-cat
            [0]
            (map #(inc (nth b %)) (range))
   ))

=> (take 5 b)

IndexOutOfBoundsException   clojure.lang.RT.nthFrom (RT.java:773)

我希望第二个示例以相同的方式运行,使用 b 的第一个元素计算第二个,然后使用第二个计算第三个。我的理解是,clojure 甚至不会尝试计算 b 的第三个元素,直到它已经为第二个元素分配了一个值并将其打印在屏幕上。

我希望有人能解释一下这里的幕后实际情况。

谢谢:)

【问题讨论】:

    标签: clojure sequence lazy-evaluation


    【解决方案1】:

    这种行为的原因是map 函数实现最简单的(map f colls) 情况。看看区别:

    user=> (def b (lazy-cat [0] (map (fn [i _] (inc (nth b i))) (range) (range))))
    #'user/b
    user=> (take 5 b)
    (0 1 2 3 4)
    

    这有点令人困惑,但让我解释一下发生了什么。那么,为什么 map 的第二个参数会改变行为:

    https://github.com/clojure/clojure/blob/master/src/clj/clojure/core.clj#L2469

    (defn map
      ...
      ([f coll]
       (lazy-seq
        (when-let [s (seq coll)]
          (if (chunked-seq? s)
            (let [c (chunk-first s)
                  size (int (count c))
                  b (chunk-buffer size)]
              (dotimes [i size]
                  (chunk-append b (f (.nth c i))))
                  (chunk-cons (chunk b) (map f (chunk-rest s))))
            (cons (f (first s)) (map f (rest s)))))))
      ([f c1 c2]
       (lazy-seq
        (let [s1 (seq c1) s2 (seq c2)]
          (when (and s1 s2)
            (cons (f (first s1) (first s2))
                  (map f (rest s1) (rest s2)))))))
    ...
    

    答案:chunked-seq 的优化原因。

    user=> (chunked-seq? (seq (range)))
    true
    

    因此,值将被“预先计算”:

    user=> (def b (lazy-cat [0] (map print (range))))
    #'user/b
    user=> (take 5 b)
    (0123456789101112131415161718192021222324252627282930310 nil nil nil nil)
    

    当然,在你的情况下,这个“预计算”失败,IndexOutOfBoundsException

    【讨论】:

    • 刚刚意识到我忘了感谢你。这是一个很大的帮助:)
    【解决方案2】:

    take的出处:

    (defn take
      "Returns a lazy sequence of the first n items in coll, or all items if
      there are fewer than n."
      {:added "1.0"
       :static true}
      [n coll]
      (lazy-seq
       (when (pos? n) 
         (when-let [s (seq coll)]
          (cons (first s) (take (dec n) (rest s)))))))
    

    现在运行第一种情况。没有机会处理越界的数组。

    对于您的第二个示例,您正在对尚未扩展为 n 个元素的序列调用 nth。 b 将尝试将 0 与依赖于不存在的元素的序列连接。

    【讨论】:

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