我正在学校学习clojure,我的考试即将开始。我只是在做一些事情,以确保我掌握它。
我试图逐行读取文件,而且正如我所做的那样,只要有&#34 ;;"我就想分割行。
到目前为止,这是我的代码
(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;"
我错过了什么吗?
答案 0 :(得分: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))))))
现在,您可以根据输入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了解详情。
答案 1 :(得分:8)
TL; DR拥抱REPL并拥抱不变性
你的问题是“我错过了什么?”而且我会说你错过了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)
关于样式的最后一个注释,对于你想要坚持kebab case的Clojure函数,所以readFile
应该是read-file
答案 2 :(得分:-1)
我会保持简单,并将其编码为:
(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
的{{1}}函数:
result-flat
您也可以使用(is= result-flat
["I" "am" "a" "line" "This" "is" "another" "one" "Followed" "by" "this"])))
,如下所示:
apply concat
如果您想首先避免构建2D矩阵,可以通过(is= (apply concat result) result-flat)
和generator function
from the Tupelo library使用lazy-gen
(一个Python):
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
函数会将每个单词放入输出延迟序列。