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

同样,当在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-seqeduction的行为似乎是不一致的或错误的。

理想情况下,其行为应该是在以下之一:

  1. str不会使lazy-seqeduction实化自身,但仍然只串行化它们的类型。这里的想法是,str不会实现“待处理”的计算,因此它可以在无限的lazy-seq或重复的eduction上进行安全使用。
  2. str始终实现“待处理”的计算,因此lazy-seqeduction都会串行化成类似于其他集合和序列的方式进行,更类似于将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-seqeduction嵌套在其他集合或序列内部时,其行为似乎是不同的;在这些情况下,它们将以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中似乎也是一样的。

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类定义的默认Java identityHashCode值,并且没有被重写:https://docs.oracle.com/javase/7/docs/api/java/lang/System.html#identityHashCode(java.lang.Object)

我明白了,知道了,所以从某种意义上说,它们都将作为类型 + hashCode 部分串行化,但它们的 hashCode 实现不同。也许这是 lazy-seq 的实现,这也迫使它强制实现它?
是的,对 lazy sequences 调用的 `hashCode` 方法会强制所有的序列都被实现,并且调用 `hashCode` 对每个元素进行调用,与 `clojure.core/hash` 类似。他们只是对 Clojure 的大多数集合计算不同的值,因为与 `hashCode` 相比,Clojure 1.6.0 改进了 `clojure.core/hash` 退出的几种类型 Clojure 集合值的多样性,对某些类型的集合(例如2个或3个元素的向量/序列,所有整数)有点低,`hashCode` 的多样性较低。
一个小提醒:`realized?` 报告的是你可能会认为是 lazy seq 的头部元素 - 不是整个东西。这是因为,当然,没有理由认为 lazy seq 的尾部也应该是懒惰的。

    (let [a (map inc (range))]
      [(realized? a)
       (first a)
       (realized? a)
       (realized? (rest a))])
0
...