对于集合和序列,当你调用 str
时,Clojure 将返回它们的 EDN-like
字符串表示形式,这比 pr-str
的结果更接近。
(str (vector 1 2 3))
"[1 2 3]"
(str '(1 2 3))
"(1 2 3)"
(str (seq [1 2 3]))
"(1 2 3)"
(str {:a 1 :b 2})
"{:a 1, :b 2}"
(str #{1 2})
"#{1 2}"
但是,当用于 lazy-seq
时,str
将打印出 lazy-seq
的类型,后面跟着其实际值的哈希值。
(str (lazy-seq [1 2 3]))
"clojure.lang.LazySeq@7861"
这不仅可能被认为是一种对大多数用例相当无用的行为(因为大多数人可能更希望它们像 seq
一样字符串化。它还会导致 lazy-seq
触发。
(let [ls (map inc [1 2 3])]
(realized? ls))
;;=> false
(let [ls (map inc [1 2 3])]
(str ls)
(realized? ls))
;;=> true
同样,当用于 eductions
时,str
将打印出 eduction
的类型,这一次后面跟着内存位置。
(str (eduction identity [1 2 3]))
"clojure.core.Eduction@2a85e4f4"
与 lazy-seq
不同,它不会“实现”eduction。
(str (eduction (fn[e] (println e) (identity e)) [1 2 3]))
"clojure.core.Eduction@26ae9861"
(str (map (fn[e] (println e) (identity e)) [1 2 3]))
1
2
3
"clojure.lang.LazySeq@7861"
目前看来,lazy-seq
和 eduction
的行为要么是不一致的,要么是错误的。
理想情况下,它应该具有以下行为之一:
str
不会使 lazy-seq
或 eduction
自身实现,但仍然只字符串化为它们的类型。这里的想法是,str
不实现“挂起的”计算,因此它在无限的 lazy-seq
上或反复在 eduction
上使用时是安全的。
str
总是实现“挂起的”计算,因此 lazy-seq
和 eduction
将像其他集合和序列一样字符串化,与对它们调用 pr-str
更相似。
请注意:在做出决定时,考虑到 ClojureScript 目前对 lazy-seq
处理不同,它选择第 2 项,对于 eduction
我不确定它如何处理,这可能很重要。
cljs.user=> (str (lazy-seq [1 2 3]))
"(1 2 3)"
cljs.user=> (str (eduction identity [1 2 3]))
"[object Object]"
附言2:似乎当 lazy-seq
或 eduction
嵌套在其他集合或序列内时,它们的行为也会不同,在这些情况下,它们将以 EDN-like
的方式字符串化。
(str [1 (map identity [2 3]) 4])
"[1 (2 3) 4]"
(str (seq [1 (map identity [2 3]) 4]))
"(1 (2 3) 4)"
(str [1 (eduction identity [2 3]) 4])
"[1 (2 3) 4]"
(str (seq [1 (eduction identity [2 3]) 4]))
"(1 (2 3) 4)"
这同样适用于 ClojureScript。