2024年Clojure调查问卷!中分享您的想法。

欢迎!请查看关于页面以了解更多关于其工作方式的信息。

+1投票
序列
编辑

对于集合和序列,在调用它们上的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

类似地,当用于eduction时,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-seqeduction行为似乎要么不一致,要么不正确。

理想情况下,它应该如下行为之一

  1. str不会使lazy-seqeduction实现自己,但同时会仅将它们的类型字符串化。这里的想法是str不会实现“挂起”的计算,因此可以在无限lazy-seq或重复使用eduction时安全使用。
  2. str总是会实现“挂起的计算”,因此lazy-seqeduction将以与其他集合和序列相同的方式字符串化,这与对它们调用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-seqeduction嵌套在其他集合或序列中时,它们的字符串化会以类似EDN的方式发生,这在这些场景中出现。

(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中也同样适用。

3 答案

0 投票

你近末尾的句子说“因为它为lazy-seq做了选项1”。你是指选项2,这似乎是从示例ClojureScript REPL交互中出现的?

是的,你抓住了这个好点子,我已经更正为选项2。
0 投票

关于微小的评论(即根本不影响所请更改行为的实质的评论——只是关于当前实现细节的注释):你显示的输出示例中打印出的所有十六进制数都是Java hashCode方法的返回值,以十六进制形式表示,通常可能与clojure.core/hash的返回值不同,或者可能相同,这取决于对象的类型以及是否定义了与hashCode不同的hasheq

对于你提供的懒序列示例,值是不可变的,所以hashCode方法的返回值是不可变集合内容的纯函数。

对于打印的eduction值,它仍然是hashCode的返回值,但在这个类中,hashCode的返回值是Java java.lang.Object类定义的默认identityHashCode值,并且没有被重写:[点此查看](https://docs.oracle.com/javase/7/docs/api/java/lang/System.html#identityHashCode(java.lang.Object))

作者
我明白了,很高兴知道这一点,所以从某种意义上说,它们都以类型+哈希码字符串化,但是它们的哈希码实现不同。或许这正是 lazy-seq 的实现,强迫它进行实际的实例化?
作者
是的,在懒序列上调用 `hashCode` 方法会导致整个序列实际化,并且会对每个元素调用 `hashCode`,类似于 `clojure.core/hash` 的方式。他们只是为 Clojure 大多数集合计算不同的值,因为与 `hashCode` 相比,Clojure 1.6.0 通过改进了几种类型 Clojure 集合的 `clojure.core/hash` 返回值的多样性,以及某些集合类型值(例如,2个或3个元素的向量/序列、所有整数)在 `hashCode` 中的多样性相对较低。
作者
一个小的说明:`realized?` 报告的是可能被认为是懒序列的“头部元素”的情况——而不是整个序列。这是因为当然没有任何理由说明懒序列的尾部也应该懒。

    (let [a (map inc (range))]
      [(realized? a)
       (first a)
       (realized? a)
       (realized? (rest a))])
0 投票
作者
...