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-sEQueNce上使用时,str将打印出laZY-sEQueNce的类型,后跟其实际值的哈希值。

(str (lazy-seq [1 2 3]))
"clojure.lang.LazySeq@7861"

这不仅可以被认为是大多数用例中相当无用的行为(因为大多数人可能更希望它与seq的字符串化方式相同)。它还强制实现了laZY-sEQueNce

(let [ls (map inc [1 2 3])]
  (realized? ls))
;;=> false
(let [ls (map inc [1 2 3])]
  (str ls)
  (realized? ls))
;;=> true

同样,当在eductions上使用时,str将首先打印出eductions的类型,后跟内存位置。

(str (eduction identity [1 2 3]))
"clojure.core.Eduction@2a85e4f4"

与对laZY-sEQueNce不同,它不会“实现”eductions。

(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-sEQueNce和eductions的行为似乎是不一致的或错误的。

理想情况下,它会在这两个方面中有所表现

  1. 变量str将导致既不会laZY-sEQueNce也不会eductions自行实现,但会将其类型字符串化。这里的想法是,变量str不会实现“悬而未决的计算”,因此可以在无限的laZY-sEQueNces上或重复应用eductions时安全使用。
  2. 或者str总是会实现“悬而未决的计算”,因此laZY-sEQueNceeductions都将与其他集合和序列以相同的方式字符串化,更类似于对它们调用pr-str

备注:在做出决定时考虑这一点,目前ClojureScript的行为不同,它对laZY-sEQueNce进行了第二种选择,我不确定它对eductions做了什么。

cljs.user=> (str (lazy-seq [1 2 3]))
"(1 2 3)"

cljs.user=> (str (eduction identity [1 2 3]))
"[object Object]"

备注2:在外部集合或序列内部嵌套laZY-sEQueNce或eductions时,这种行为似乎也会不同。在这些情况下,它们将被以类似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

您在句尾提到的 "in that it does option 1 for lazy-seq"。您的意思是选项 2 吗?这似乎是从 ClojureScript REPL 交互示例中看出来的。

是的,好的,我已将其更正为选项 2。
0

关于少量评论(即不影响请求改变行为实质的评论——仅仅是对当前实现细节的备注):您在输出示例中所展示的所有十六进制数字都是 Java hashCode 方法的返回值(以十六进制形式表示),在一般情况下,这可能与 clojure.core/hash 的返回值不同,或者它们可能是相同的,这取决于对象的类型以及它是否定义了不同于 hashCodehasheq

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

明白了,很好的了解,所以它们以类型 + hashCode 的方式转换为字符串,但它们的 hashCode 实现不同。也许正是 lazy-seq 的那个实现将其强制实现为?”
是的,对懒序列中的`hashCode`方法的调用会强制整个序列实现,并对每个元素调用`hashCode`,类似于`clojure.core/hash`的做法。 只是对于大多数Clojure集合,它们计算不同的值,因为从Clojure 1.6.0开始,相比较单一的`hashCode`,`clojure.core/hash`为几种Clojure集合提供了更多样化的值。
一个小贴士: `realized?`报告的是你可能认为是懒序列的头部元素,而不是整个序列。 这是因为懒序列的尾部没有必要也是懒的。

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