Попытка разбить строку в Clojure сталкивается с проблемой ленивого seq

Я работаю над проблемой чтения файла с такими строками, как:

A abcdefg
B bcdefgh

Но я продолжаю получать ошибки о Lazy Sequence, несовместимой с Java Charseq ..

Я пытался:

(def notlazy (doall lyne2))

Потом подумал, что проверил:

(realized? notlazy)
true

Но все равно:

(str/split notlazy #" ")
ClassCastException class clojure.lang.LazySeq cannot be cast to class
  java.lang.CharSequence (clojure.lang.LazySeq is in unnamed module of
  loader 'app'; java.lang.CharSequence is in module java.base of loader
  'bootstrap')  clojure.string/split (string.clj:219)

Помоги пожалуйста!


person Mark McWiggins    schedule 08.12.2020    source источник


Ответы (2)


Первый аргумент str/split должен быть CharSequence для разделения. Предположительно, вы хотите разделить каждую входную строку в последовательности, для которой вы можете использовать map без необходимости нетерпеливо оценивать входную последовательность:

(map (fn [line] (str/split line #" ")) lyne2)
person Lee    schedule 08.12.2020

Немного расширяя предыдущий результат, у нас есть этот пример. Вы можете воспроизвести следующее, используя этот шаблон проекта.

(ns tst.demo.core
  (:use demo.core tupelo.core tupelo.test)
  (:require
    [clojure.java.io :as io]
    [tupelo.string :as str]
    ))

(def data-file
  "A abcdefg
   B    bcdefgh
  ")

(dotest
  ; Version #1
  (let [lines      (line-seq (io/reader (str/string->stream data-file)))
        lines2     (remove str/blank? lines)
        lines3     (map str/trim lines2)
        line-words (mapv #(str/split % #"\s+") lines3) ; "\s+" => "one or more whitespace chars"
        ]
    (spyxx lines)
    (spyxx lines2)
    (spyxx lines3)
    (spyxx line-words))

с результатом:

--------------------------------------
   Clojure 1.10.2-alpha1    Java 15
--------------------------------------

Testing tst.demo.core
lines       => <#clojure.lang.Cons ("A abcdefg" "   B    bcdefgh" "  ")>
lines2      => <#clojure.lang.LazySeq ("A abcdefg" "   B    bcdefgh")>
lines3      => <#clojure.lang.LazySeq ("A abcdefg" "B    bcdefgh")>
line-words  => <#clojure.lang.PersistentVector [["A" "abcdefg"] ["B" "bcdefgh"]]>

Это показывает тип каждого результата вместе с его значением. Мы используем string->stream, поэтому нам не нужно создавать фиктивный файл для чтения.

Ниже показано, как это обычно пишется в реальном коде (а не в качестве демонстрационного упражнения, как в версии №1). Мы используем оператор последнего потока и пишем модульный тест для проверки результата:

  ; Version #2
  (let [result (->> data-file
                 (str/string->stream)
                 (io/reader)
                 (line-seq)
                 (remove str/blank?)
                 (map str/trim)
                 (mapv #(str/split % #"\s+"))) ; "\s+" => "one or more whitespace chars"
        ]
    (is= result [["A" "abcdefg"] ["B" "bcdefgh"]])))
person Alan Thompson    schedule 08.12.2020