对于集合和序列,当在它们上调用str
时,Clojure会返回它们的字符串表示形式,类似于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
的行为不同,它选择了第二个选项,对于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中似乎也是一样的。