2024 Clojure状态调查中分享您的看法!

欢迎!请参见关于页面以获取更多有关如何使用本站的信息。

+1
序列
编辑

对于集合和序列,当在它们上调用str时,Clojure将返回一个类似于EDN的字符串表示形式,这更类似于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-seq和eduction的行为似乎是既不一致也不正确。

理想情况下,其行为将是这样的:

  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-seq或eduction嵌套在另一个集合或序列中时,它们的行为似乎不同,在这种情况下,它们将根据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

对于您提供的lazy seq示例,其值是不可变的,因此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))

我明白了,知道了很好,从某种意义上说,它们都将内容转换为类型 + hashCode,但它们的hashCode实现方式不同。也许这就是为什么lazy-seq也强制实现在那里呢?
by
是的,在惰性序列上调用的`hashCode`方法将迫使整个序列实现,并对每个元素调用`hashCode`,类似于`clojure.core/hash`的方式。不过,对于大多数Clojure集合,它们计算的值不同,因为从Clojure 1.6.0开始,`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
...