File io 从文件读取时拆分clojure中的行
我正在学校学习clojure,我马上就要考试了。我只是在做一些事情,以确保我掌握了窍门 我正试图逐行读取文件,正如我所做的那样,每当有“;”时,我都要拆分该行 这是到目前为止我的代码File io 从文件读取时拆分clojure中的行,file-io,clojure,split,File Io,Clojure,Split,我正在学校学习clojure,我马上就要考试了。我只是在做一些事情,以确保我掌握了窍门 我正试图逐行读取文件,正如我所做的那样,每当有“;”时,我都要拆分该行 这是到目前为止我的代码 (defn readFile [] (map (fn [line] (clojure.string/split line #";")) (with-open [rdr (reader "C:/Users/Rohil/Documents/work.txt.txt")] (doseq [line (li
(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;"
我错过了什么吗;DR拥抱REPL,拥抱不变性 你的问题是“我遗漏了什么?”对此,我想说你遗漏了Clojure最好的功能之一,REPL 编辑:您可能还缺少Clojure使用不可变数据结构 考虑以下代码片段:
(doseq [x [1 2 3]]
(inc x)
(prn x))
(doseq [x [1 2 3]] (prn (inc x)))
此代码不打印“2 3 4”
它打印“1 2 3”,因为x不是可变变量
在第一次迭代中,(inc x)
被调用,返回2,由于没有传递给任何对象,因此被丢弃,然后(prn x)
打印x的值,该值仍然是1
现在考虑这个代码片段:
(doseq [x [1 2 3]]
(inc x)
(prn x))
(doseq [x [1 2 3]] (prn (inc x)))
在第一次迭代中,inc将其返回值传递给prn,因此得到2
长示例:
我不想剥夺你自己解决问题的机会,所以我将用一个不同的问题作为例子
给定文件“birds.txt”
使用数据“1chicken\n2duck\n3larry”
您需要编写一个函数,该函数接受一个文件并返回一系列鸟名
让我们将此问题分解为更小的块:
首先让我们读取文件并将其拆分为行
(slurp“birds.txt”)
将为整个文件提供一个字符串
clojure.string/split line
将为我们提供一个集合,其中每一行作为集合中的一个元素
(clojure.string/split-lines(slurp“birds.txt”)
让我们[“chicken”“2duck”“3Larry”]
此时,我们可以在该集合上映射一些函数,以去除数字,如(map#(clojure.string/replace%#“\d”“”)birds集合)
或者,当整个文件是一个字符串时,我们可以将这一步骤向上移动
现在我们已经有了所有的片段,我们可以将它们放在一个功能管道中,其中一个片段的结果将输入下一个片段
在Clojure中,有一个很好的宏使其更具可读性,即->
宏
它接受一次计算的结果,并将其作为第一个参数注入下一次计算
因此,我们的管道如下所示:
(-> "C:/birds.txt"
slurp
(clojure.string/replace #"\d" "")
clojure.string/split-lines)
(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"]])
关于样式的最后一个注释,对于Cujjor函数,你想坚持“代码>阅读文件应该是代码>读取文件< /COD> < /P> < P> >我不确定你是否需要在学校,但是既然加里已经给出了一个很好的答案,考虑这是一个额外的奖励。 您可以使用转换器对文本行进行优雅的转换。您需要的成分是,允许您将这些行视为可还原的集合,并在您完成还原时关闭读者:
(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
对所有“拆分”的长度求和。
(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")))
或与同时使用:
(transduce
(comp
(mapcat #(str/split % #";"))
(map count)
(take-while #(> 5 %)))
+
(lines-reducible (io/reader "/tmp/work.txt")))
请阅读以了解更多详细信息。我会保持它的简单性,并将其编码如下:
(-> "C:/birds.txt"
slurp
(clojure.string/replace #"\d" "")
clojure.string/split-lines)
(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)矩阵。撤消此操作的最简单方法是使用展平
函数生成结果展平
:
(is= result-flat
["I" "am" "a" "line" "This" "is" "another" "one" "Followed" "by" "this"])))
您还可以使用apply concat
,如下所示:
(is= (apply concat result) result-flat)
如果您想避免首先构建2D矩阵,可以通过lazy gen
和yield
使用生成器函数(一种la Python):
在这种情况下,lazy gen
创建生成器函数。
请注意,for
的已替换为doseq
,并且yield
函数将每个单词放入输出延迟序列。打印行,但不打印分割结果。使用(doto(split…)。谢谢!再加上cfrick所说的,这是因为您的分割结果被丢弃了,因为行是不可变的。您需要将结果传递给println。查看我的答案了解更多详细信息生成器在某些方面可能很方便,但我认为在这种情况下,我会坚持使用mapcat
来避免嵌套序列。当我只使用line seq
而不是line reducable
时,有什么区别?也许提前终止?好问题。使用line seq
时,您将创建中间聚合,并因此进行更多的垃圾收集。此外,您还必须自己关闭读卡器(通常使用打开的来完成)。这是为什么?我认为transduce
将确保不创建中间集合。通过使用line-seq
而不是line-reduceable
,我的意思是使用(几乎)与您所写的完全相同的表达式:(transduce(comp…(line-seq…
现在我明白了您的意思。(transduce(comp…+(line-seq(io/reader)/tmp/work.txt))也可以工作,但需要考虑两件事:1)您必须自己关闭读卡器2)line seq仍将创建一个中间延迟序列。如果lines Reduceable,则不会创建此序列。