Как я могу преобразовать LazySeq символов в строку в Clojure?
Допустим, у меня есть LazySeq java.lang. Характер как
(\b \ \! \/ \b \ \% \1 \9 \/ \. \i \% \$ \i \space \^@)
Как я могу преобразовать это в строку? Я попробовал очевидное
(String. my-char-seq)
но это бросает
java.lang.IllegalArgumentException: No matching ctor found for class java.lang.String (NO_SOURCE_FILE:0)
[Thrown class clojure.lang.Compiler$CompilerException]
Я думаю, потому что конструктор String ожидает примитивный символ char[] вместо LazySeq. Итак, я попробовал что-то вроде
(String. (into-array my-char-seq))
но это бросает то же исключение. Теперь проблема в том, что в массив возвращается java.lang.Character[] вместо примитивного символа []. Это расстраивает, потому что я на самом деле генерирую свою последовательность символов, как это
(map #(char (Integer. %)) seq-of-ascii-ints)
По сути, у меня есть последовательность целых чисел, представляющих символы ASCII; 65 = A и т. Д. Вы можете видеть, что я явно использую функцию приведения типа примитива (char x).
Это означает, что моя функция карты возвращает примитивный символ, но функция карты Clojure в целом возвращает объект java.lang.Character.
3 ответа
Это работает:
(apply str my-char-seq)
По сути, str вызывает toString() для каждого из своих аргументов, а затем объединяет их. Здесь мы используем apply для передачи символов в последовательности как аргументов в str.
Другой способ заключается в использовании clojure.string/join
, следующее:
(require '[clojure.string :as str] )
(assert (= (vec "abcd") [\a \b \c \d] ))
(assert (= (str/join (vec "abcd")) "abcd" ))
(assert (= (apply str (vec "abcd")) "abcd" ))
Существует альтернативная форма clojure.string/join
который принимает разделитель. Увидеть:
http://clojuredocs.org/clojure_core/clojure.string/join
Для более сложных проблем, вы также можете посмотреть strcat
из библиотеки Тупело:
(require '[tupelo.core :as t] )
(prn (t/strcat "I " [ \h \a nil \v [\e \space (byte-array [97])
[ nil 32 "complicated" (Math/pow 2 5) '( "str" nil "ing") ]]] ))
;=> "I have a complicated string"
Как частный случай, если основной тип рассматриваемой последовательности clojure.lang.StringSeq
Вы также можете сделать:
(.s (my-seq))
который чрезвычайно эффективен, поскольку он просто вытаскивает открытое окончательное поле CharSequence из класса clojure StringSeq.
Пример:
(type (seq "foo"))
=> clojure.lang.StringSeq
(.s (seq "foo"))
=> "foo"
(type (.s (seq "foo")))
=> java.lang.String
Пример значений времени (и обратите внимание на разницу при использовании подсказки типа):
(time
(let [q (seq "xxxxxxxxxxxxxxxxxxxx")]
(dotimes [_ 1000000]
(apply str q))))
"Elapsed time: 620.943971 msecs"
=> nil
(time
(let [q (seq "xxxxxxxxxxxxxxxxxxxx")]
(dotimes [_ 1000000]
(.s q))))
"Elapsed time: 1232.119319 msecs"
=> nil
(time
(let [^StringSeq q (seq "xxxxxxxxxxxxxxxxxxxx")]
(dotimes [_ 1000000]
(.s q))))
"Elapsed time: 3.339613 msecs"
=> nil