【发布时间】:2019-08-14 13:00:35
【问题描述】:
我一直在测试concat 的行为。
文档字符串说:
返回一个惰性序列,表示元素的串联 提供的 colls。
然而,concat 似乎并没有因为它的论点而表现得很懒惰。相反,我们观察到通常的热切评估。这不是我所期望的。
观察:
这里是生成二叉树的简单代码,该二叉树包含来自“The Joy of Clojure, 2nd edition”, p. 中的整数。 208:
; we have a binary tree based on records, holding a val and having left
; and right subtrees
(defrecord TreeNode [val left right])
; xconj basically is insertion sort; inserts value v into tree t.
; + The code in JoC is more compact; here, "explicited" for readability.
(defn xconj [t v]
(cond
(nil? t) (TreeNode. v nil nil)
(< v (get t :val)) (TreeNode. (get t :val)
(xconj (get t :left) v)
(get t :right))
:else (TreeNode. (get t :val)
(get t :left)
(xconj (get t :right) v))))
; Convert a tree into a seqs (in-order traversal, so the seq will spit
; out the integers in order sorted ascending).
; Returns a lazy seq as "concat" returns clojure.lang.LazySeq
; + The code in JoC is more compact; here, "explicited" for readability.
(defn xseq [t]
(when (some? t)
(concat (xseq (get t :left))
[ (get t :val) ]
(xseq (get t :right)))))
; "xseq" is a bit mute; add some printout to probe behaviour (watching
; out to not destroy laziness when doing so)
(defn xseq-p1 [t k]
(if (nil? t) (println k "▼" "⊥") (println k "▼" (get t :val)))
(when (some? t)
(concat (xseq-p1 (get t :left) (str k "[" (get t :val) "]" "◀"))
[ (get t :val) ]
(xseq-p1 (get t :right) (str k "[" (get t :val) "]" "▶")))))
; create a tree for testing
(def ll (reduce xconj nil [3 5 2 4 6]))
现在,查询xseq-p1返回的值的类型,显示遍历了整棵树?!
([3]◀[2]▶ ▼ ⊥ 表示找到 3,向左,找到 2,向右,现在为零)
(type (xseq-p1 ll ""))
; ▼ 3
; [3]◀ ▼ 2
; [3]◀[2]◀ ▼ ⊥
; [3]◀[2]▶ ▼ ⊥
; [3]▶ ▼ 5
; [3]▶[5]◀ ▼ 4
; [3]▶[5]◀[4]◀ ▼ ⊥
; [3]▶[5]◀[4]▶ ▼ ⊥
; [3]▶[5]▶ ▼ 6
; [3]▶[5]▶[6]◀ ▼ ⊥
; [3]▶[5]▶[6]▶ ▼ ⊥
; clojure.lang.LazySeq
让xseq 变得懒惰需要在concat 前面多加一个lazy-seq:
(defn xseq-p2 [t k]
(if (nil? t) (println k "▼" "⊥") (println k "▼" (get t :val)))
(when (some? t)
(lazy-seq
(concat (xseq-p2 (get t :left) (str k "[" (get t :val) "]" "◀"))
[ (get t :val) ]
(xseq-p2 (get t :right) (str k "[" (get t :val) "]" "▶"))))))
现在它很懒:
(type (xseq-p2 ll ""))
; ▼ 3
; clojure.lang.LazySeq
(take 2 (xseq-p2 ll ""))
; ▼ 3
; ([3]◀ ▼ 2
; [3]▶ ▼ 5
; [3]◀[2]◀ ▼ ⊥
; [3]◀[2]▶ ▼ ⊥
; 2 3)
这是预期的吗?
附言
另一种方法是延迟两个下降(或仅向右下降)。两种下降都懒惰,xseq-p3 甚至比xseq-p1 更懒惰:
(defn xseq-p3 [t k]
(if (nil? t) (println k "▼" "⊥") (println k "▼" (get t :val)))
(when (some? t)
(let [ left (get t :left)
v (get t :val)
right (get t :right)
l-seq (lazy-seq (xseq-p3 left (str k "[" v "]" "◀")))
r-seq (lazy-seq (xseq-p3 right (str k "[" v "]" "▶"))) ]
(concat l-seq [v] r-seq))))
(type (xseq-p3 ll ""))
; ▼ 3
; clojure.lang.LazySeq
(take 2 (xseq-p3 ll ""))
; ▼ 3
; ([3]◀ ▼ 2
; [3]◀[2]◀ ▼ ⊥
; [3]◀[2]▶ ▼ ⊥
; 2 3)
【问题讨论】:
-
lazy-cat可能会有所帮助。 -
@glts 啊,是的,在
xseq-p1中使用lazy-cat而不是cat使其表现得像xseq-p3!那是一场胜利。但是,concat的“懒惰”是什么?我想说,只有当它的论点一开始是懒惰的时候,它才会表现得懒惰。