在 REPL 中实现 Clojure 惰性序列(字符串)

Realizing a Clojure lazy sequence (string) in the REPL

我试图在 REPL 中实现惰性序列(应该生成单个字符串),但没有成功。原始代码工作正常:

(def word_list ["alpha" "beta" "gamma" "beta" "alpha" "alpha" "beta" "beta" "beta"])
(def word_string (reduce str (interpose " " word_list))); 
word_string ; "alpha beta gamma beta alpha alpha beta beta beta"

但不想留下足够好,我想知道还有什么可以工作,并尝试删除 reduce,认为 str 可能有相同的效果。它没有...

(def word_string (str (interpose " " word_list)))
word_string ; "clojure.lang.LazySeq@304a9790"

我尝试了显而易见的方法,再次使用 reduce,但这也没有用。 another question 关于实现看起来很有希望的惰性序列,但我尝试过的都不起作用:

(reduce str word_string)   ; "clojure.lang.LazySeq@304a9790"
(apply str word_string)    ; "clojure.lang.LazySeq@304a9790"
(println word_string)      ; "clojure.lang.LazySeq@304a9790"
(apply list word_string)   ; [\c \l \o \j \u \r \e \. \l \a \n \g \. \L \a \z \y...]
(vec word_string)          ; [\c \l \o \j \u \r \e \. \l \a \n \g \. \L \a \z \y...]
(apply list word_string)   ; (\c \l \o \j \u \r \e \. \l \a \n \g \. \L \a \z \y...)
(take 100 word_string)     ; (\c \l \o \j \u \r \e \. \l \a \n \g \. \L \a \z \y...)

事实上,一些变体在 "clojure.lang.LazySeq" 中给了我 个字符 也让我担心 - 我是否以某种方式丢失了实际的字符串值,而我的参考只是有值"clojure.lang.LazySeq"?如果没有,我如何真正实现价值?

澄清一下:鉴于 word_string 被分配给惰性序列,我将如何实现它?比如说 (realize word_string),如果它存在的话。

更新:根据已接受的答案以及str的工作原理,事实证明我可以得到实际的序列值,而不仅仅是它的名称:

(reduce str "" word_string) ; "alpha beta gamma beta alpha alpha beta beta beta"

是的,这是糟糕的代码。 :) 我只是想了解发生了什么,为什么它坏了,以及实际值是否仍然那里

你想要的是:

(def word_string (apply str (interpose " " word_list)))

查看str的文档:

With no args, returns the empty string. With one arg x, returns
x.toString(). (str nil) returns the empty string. With more than
one arg, returns the concatenation of the str values of the args.

因此您在序列上调用 .toString,它生成该表示而不是将 str 应用于序列的元素作为参数。

顺便说一句,做你想做的更惯用的方法是:

(clojure.string/join " " word_list)

此外,字符串不是惰性序列。 interpose returns 一个惰性序列,您正在调用 .toString

在 Clojure 中实现惰性 seq 不需要做任何特别的事情,您只需在任何需要 seq 的地方使用它。

user=> (def word-list ["alpha" "beta" "gamma" "beta" "alpha" "alpha" "beta" "beta" "beta"])
#'user/word-list
user=> (def should-be-a-seq (interpose " " word_list))
#'user/should-be-a-seq
user=> (class should-be-a-seq)
clojure.lang.LazySeq

所以我们有一个 seq,如果我在任何情况下使用它都会遍历 seq 中的所有值,它最终会完全实现。例如

user=> (seq should-be-a-seq)
("alpha" " " "beta" " " "gamma" " " "beta" " " "alpha" " " "alpha" " " "beta" " " "beta" " " "beta")

虽然它仍然是一个 seq,但实际上它仍然是之前的同一个对象。

user=> (str should-be-a-seq)
"clojure.lang.LazySeq@304a9790"

正如 Diego Basch 所提到的,在某物上调用 str 就像调用 .toString 一样,对于 LazySeq 方法来说,这显然只是从 Object 继承的默认 .toString。

因为它是一个 seq,所以您可以像使用任何 seq 一样使用它,无论它之前是否已经完全实现。例如

user=> (apply str should-be-a-seq)
"alpha beta gamma beta alpha alpha beta beta beta"
user=> (reduce str should-be-a-seq)
"alpha beta gamma beta alpha alpha beta beta beta"
user=> (str/join should-be-a-seq)
"alpha beta gamma beta alpha alpha beta beta beta"
user=> (str/join (map #(.toUpperCase %) should-be-a-seq))
"ALPHA BETA GAMMA BETA ALPHA ALPHA BETA BETA BETA"