2024 State of Clojure Survey!中分享您的想法。https://www.surveymonkey.com/r/clojure2024

欢迎!请查看关于页面以了解更多有关此页面如何工作的信息。

+1
Sequences
编辑

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

类似地,当用于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或反复在eductions上使用时安全使用。
  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-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中似乎也是如此。

3 答案

0

你句子末尾提到“在其中实现了lazy-seq的选项1”。你是想指选项2吗?从ClojureScript REPL交互的例子看,它看起来应该是选项2。

是的,抓到一个好点,我已将其修改为选项2。
0

一些小注释(即那些不会影响所请求的行为更改实质的注释——仅仅是对当前实现细节的笔记):你展示的输出示例中打印的所有十六进制数都是Java hashCode方法的返回值,按十六进制表示,这通常可能不同于clojure.core/hash的返回值,或者可能相同,具体取决于对象的类以及它是否对hasheq有不同于hashCode的定义。

对于你给出的lazy seq示例,其值是不可变的,所以hashCode方法的返回值是纯函数的不可变集合内容的函数。

对于打印出的eduction值,它仍然是hashCode的返回值,但对于此类,hashCode的返回值是为java.lang.Object类定义的默认Java identityHashCode值,并且没有被覆盖:[https://docs.oracle.com/javase/7/docs/api/java/lang/System.html#identityHashCode(java.lang.Object)](https://docs.oracle.com/javase/7/docs/api/java/lang/System.html#identityHashCode(java.lang.Object))

by
是的,对懒惰序列的`hashCode`方法调用会强制实化整个序列,并且要对每个元素调用`hashCode`,类似于`clojure.core/hash`的做法。但是,对于Clojure 1.6.0及以后版本中的大多数Clojure集合,它们计算出的值可能不同,因为相较于`hashCode`,`clojure.core/hash`对某些类型的Clojure集合返回值的多样性增加了,而`hashCode`对于某些类型的集合(如2个元素或3个元素的向量/序列,所有整数)其值的多样性相对较低。
by
一个小小的备注:`realized?`会报告你可能认为的懒惰序列的头元素,而不是整个序列。这是因为当然没有理由说明为什么懒惰序列的尾部也应该是懒惰的。

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