在从文件中读取时,在clojure中分割线条

use*_*010 3 file-io split clojure

我正在学校学习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)))))
Run Code Online (Sandbox Code Playgroud)

当我这样做时,我仍然得到输出:

"I;Am;A;String;"
Run Code Online (Sandbox Code Playgroud)

我错过了什么吗?

Mic*_*ent 11

我不确定你是否在学校需要这个,但由于加里已经给出了一个很好的答案,所以认为这是一个奖励.

您可以使用传感器对文本行进行优雅的转换.您需要的成分是允许您将线条视为可简化的集合,并在您完成减少后关闭阅读器:

(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))))))
Run Code Online (Sandbox Code Playgroud)

现在,您可以根据输入执行以下操作work.txt:

I;am;a;string
Next;line;please
Run Code Online (Sandbox Code Playgroud)

计算每个"分裂"的长度

(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]
Run Code Online (Sandbox Code Playgroud)

总结所有"分裂"的长度

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

将所有单词的长度相加,直到找到长于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")))
Run Code Online (Sandbox Code Playgroud)

或者take-while:

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

有关详细信息,请阅读https://tech.grammarly.com/blog/building-etl-pipelines-with-clojure.


Gar*_*ary 8

TL; DR拥抱REPL并拥抱不变性

你的问题是"我错过了什么?" 而且我会说你错过了Clojure的最佳功能之一,即REPL.

编辑:您可能也会遗漏Clojure使用不可变数据结构

考虑以下代码段:

(doseq [x [1 2 3]]
   (inc x)
   (prn x))
Run Code Online (Sandbox Code Playgroud)

此代码不打印"2 3 4"

它打印"1 2 3"因为x不是可变变量.

在第一次迭代中 (inc x)调用,返回2,然后抛出它,因为它没有被传递给任何东西,然后(prn x)打印x的值,它仍然是1.

现在考虑以下代码段:

(doseq [x [1 2 3]] (prn (inc x)))
Run Code Online (Sandbox Code Playgroud)

在第一次迭代期间,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)
Run Code Online (Sandbox Code Playgroud)

风格上最后一个音符,为Clojure的函数,你要坚持到烤肉情况下这样readFile应该是read-file