чтобы избежать вложенных последовательностей.

чаю clojure в школе, и у меня есть экзамен. Я просто работал над несколькими вещами, чтобы убедиться, что я понял это.

Я пытаюсь читать из файла строку за строкой, и, как и я, я хочу разделить строку всякий раз, когда есть «;».

Вот мой код до сих пор

(defn readFile []
  (map (fn [line] (clojure.string/split line #";"))
  (with-open [rdr (reader "C:/Users/Rohil/Documents/work.txt.txt")]
    (doseq [line (line-seq rdr)]
      (clojure.string/split line #";")
        (println line)))))

Когда я делаю это, я все еще получаю вывод:

"I;Am;A;String;"

Я что-то пропустил?

 Gary17 нояб. 2017 г., 22:58
в дополнение к тому, что сказал cfrick, это потому, что ваши результаты расщепления отбрасываются, так как строка неизменна. Вам нужно передать результаты в println. Смотрите мой ответ для более подробной информации
 username1010101010101016 нояб. 2017 г., 16:51
Чувак, это было так глупо. Большое спасибо!
 cfrick16 нояб. 2017 г., 16:49
Вы печатаете строку, но не результат разделения. использование(doto (split...) println) для отладки.

Ответы на вопрос(3)

нужно ли вам это в школе, но так как Гэри уже дал превосходный ответ, рассмотрите это как бонус.

Вы можете выполнять элегантные преобразования в строках текста с помощью преобразователей. Ингредиент, который вам нужен, - это то, что позволяет вам рассматривать строки как сокращаемую коллекцию и закрывает читателя, когда вы закончите сокращать:

(defn lines-reducible [^BufferedReader rdr]
  (reify clojure.lang.IReduceInit
    (reduce [this f init]
      (try
        (loop [state init]
          (if (reduced? state)
            @state
            (if-let [line (.readLine rdr)]
              (recur (f state line))
              state)))
        (finally
          (.close rdr))))))

Теперь вы можете сделать следующее, учитывая вводwork.txt:

I;am;a;string
Next;line;please

Подсчитайте длину каждого «разделения»

(require '[clojure.string :as str])
(require '[clojure.java.io :as io])

(into []
      (comp
       (mapcat #(str/split % #";"))
       (map count))
      (lines-reducible (io/reader "/tmp/work.txt")))
;;=> [1 2 1 6 4 4 6]

Суммируйте длину всех «разбиений»

(transduce
 (comp
  (mapcat #(str/split % #";"))
  (map count))
 +
 (lines-reducible (io/reader "/tmp/work.txt")))
;;=> 24

Суммируйте длину всех слов, пока мы не найдем слово длиннее 5

(transduce
 (comp
  (mapcat #(str/split % #";"))
  (map count))
 (fn
   ([] 0)
   ([sum] sum)
   ([sum l]
    (if (> l 5)
      (reduced sum)
      (+ sum l))))
 (lines-reducible (io/reader "/tmp/work.txt")))

или сtake-while:

(transduce
 (comp
  (mapcat #(str/split % #";"))
  (map count)
  (take-while #(> 5 %)))
 +
 (lines-reducible (io/reader "/tmp/work.txt")))

Читатьhttps://tech.grammarly.com/blog/building-etl-pipelines-with-clojure Больше подробностей.

 Juraj Martinka19 нояб. 2017 г., 08:43
Почему это? я думал чтоtransduce будет гарантировать, что промежуточные коллекции не создаются. Используяline-seq вместоlines-reducible Я имел в виду использовать (почти) те же выражения, которые вы написали:(transduce (comp ... (line-seq ...
 Michiel Borkent19 нояб. 2017 г., 14:32
Теперь я понимаю, что вы имеете в виду. (transduce (comp ...) + (line-seq (io / reader "/tmp/work.txt"))) также работает, но нужно учитывать две вещи: 1) вы должны сами закрыть читателя 2) line- seq все равно создаст промежуточную ленивую последовательность. С помощью строк-сводимых вы не будете создавать эту последовательность.
 Juraj Martinka18 нояб. 2017 г., 19:32
Какая разница, когда я просто используюline-seq вместоlines-reducible? Возможно досрочное прекращение?
 Michiel Borkent18 нояб. 2017 г., 22:06
Хороший вопрос. Когда используешьline-seq в результате вы создадите промежуточные агрегаты и получите больше мусора. Также вам придется позаботиться о том, чтобы закрыть читателя самостоятельно (часто сwith-open).

Ваш вопрос был "что мне не хватает?" и на это я бы сказал, что вам не хватает одной из лучших функций Clojure - REPL.

редактировать: Вы также можете упустить, что Clojure использует неизменяемые структуры данных, так

рассмотрите этот фрагмент кода:

(doseq [x [1 2 3]]
   (inc x)
   (prn x))

Этот код не печатает "2 3 4"

он печатает «1 2 3», потому что x не является изменяемой переменной.

Во время первой итерации(inc x) вызывается, возвращает 2, и это выбрасывается, потому что оно не было передано ни в чем, затем(prn x) печатает значение x, которое по-прежнему равно 1.

Теперь рассмотрим этот фрагмент кода:

(doseq [x [1 2 3]] (prn (inc x)))

Во время первой итерации inc передает свое возвращаемое значение в prn, так что вы получаете 2

Длинный пример:

Я не хочу лишать вас возможности решить проблему самостоятельно, поэтому я буду использовать другую проблему в качестве примера.

Учитывая файл"birds.txt" с данными"1chicken\n 2duck\n 3Larry" Вы хотите написать функцию, которая принимает файл и возвращает последовательность имен птиц

Давайте разберем эту проблему на более мелкие куски:

сначала давайте прочитаем файл и разделим его на строки

(slurp "birds.txt") даст нам весь файл строку

clojure.string/split-lines даст нам коллекцию с каждой строкой в ​​качестве элемента в коллекции

(clojure.string/split-lines (slurp "birds.txt")) получает нас["1chicken" "2duck" "3Larry"]

В этот момент мы можем отобразить какую-то функцию над этой коллекцией, чтобы вырезать число как(map #(clojure.string/replace % #"\d" "") birds-collection)

или мы можем просто переместить этот шаг вверх по конвейеру, когда весь файл представляет собой одну строку.

Теперь, когда у нас есть все наши части, мы можем собрать их вместе в функциональный конвейер, где результат одного фрагмента передается в следующий.

В Clojure есть хороший макрос, чтобы сделать его более читабельным,-> макрос

Он берет результат одного вычисления и вставляет его в качестве первого аргумента для следующего

так что наш конвейер выглядит так:

(-> "C:/birds.txt"
     slurp
     (clojure.string/replace #"\d" "") 
     clojure.string/split-lines)

Последнее замечание о стиле, для функций Clojure, которые вы хотите придерживатьсякебаб такreadFile должно бытьread-file

 Gary17 нояб. 2017 г., 21:48
Да, шашлык. Видеть:wiki.c2.com/?KebabCase

(ns tst.demo.core
  (:use tupelo.test)
  (:require [tupelo.core :as t]
            [clojure.string :as str] ))
(def text
 "I;am;a;line;
  This;is;another;one
  Followed;by;this;")

(def tmp-file-name "/tmp/lines.txt")

(dotest
  (spit tmp-file-name text) ; write it to a tmp file
  (let [lines       (str/split-lines (slurp tmp-file-name))
        result      (for [line lines]
                      (for [word (str/split line #";")]
                        (str/trim word)))
        result-flat (flatten result)]
(is= result
  [["I" "am" "a" "line"]
   ["This" "is" "another" "one"]
   ["Followed" "by" "this"]])

Заметить, чтоresult является двухслойной (2D) матрицей слов. Самый простой способ отменить этоflatten функция для производстваresult-flat:

(is= result-flat
  ["I" "am" "a" "line" "This" "is" "another" "one" "Followed" "by" "this"])))

Вы также можете использоватьapply concat как в:

(is= (apply concat result) result-flat)

Если вы хотите избежать создания 2D-матрицы, вы можете использоватьgenerator function (а-ля Питон) черезlazy-gen а такжеyield из библиотеки Тупело:

(dotest
  (spit tmp-file-name text) ; write it to a tmp file
  (let [lines  (str/split-lines (slurp tmp-file-name))
        result (t/lazy-gen
                 (doseq [line lines]
                   (let [words (str/split line #";")]
                     (doseq [word words]
                       (t/yield (str/trim word))))))]

(is= result
  ["I" "am" "a" "line" "This" "is" "another" "one" "Followed" "by" "this"])))

В этом случае,lazy-gen создает функцию генератора. Заметить, чтоfor был заменен наdoseqиyield Функция помещает каждое слово в ленивую последовательность вывода.

 Jeff Terrell Ph.D.17 нояб. 2017 г., 22:35
Генераторы могут быть удобны для некоторых вещей, но я думаю, что в этом случае я бы просто придерживалсяmapcat чтобы избежать вложенных последовательностей.

Ваш ответ на вопрос