对于集合和序列,当在对它们调用 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 中看起来是这样。