下面是一个github 读取一段base64加密的文件,并将解码的文件写入到一个随机的临时文件 。
入口函数是:(read-tran-file)
(defn tran-b64 [f s]
(when-let [b (String. (b64/decode (.getBytes s)))]
(with-open [w (io/writer f :append true)]
(.write w b)
)
)
)
(defn gen-tmp-file []
"gen temp file"
(str
(System/getProperty "java.io.tmpdir")
(System/getProperty "file.separator")
(java.util.UUID/randomUUID) ".tmp")
)
(defn tran [f x]
(with-open [rdr (io/reader x)]
(map (partial tran-b64 f) (doall (line-seq rdr)))
)
)
(defn read-tran-file []
(let [f (gen-tmp-file)]
(tran f "https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt")
)
)
请忽略(tran-b64)
中反复打开流的动作。
那么问题来了:
(defn tran [f x]
(with-open [rdr (io/reader x)]
(map (partial tran-b64 f) (doall (line-seq rdr)))
)
)
这个函数的with-open中,(map (partial tran-b64 f) (doall (line-seq rdr)))
后面还加上其他函数执行,如:
(map (partial tran-b64 f) (doall (line-seq rdr)))
(println f)
都会导致(map (partial tran-b64 f) (doall (line-seq rdr)))
这一行代码不执行。
即使使用(do (map *) (println *)) 也不行。
到底这个懒序列(lazy-seq)是使用doall进行读取吗?还是使用dorun?
我看了doall函数调用的是dorun,而dorun使用的next方法。
那么这里又有一个问题<<clojure编程乐趣>>书中说的lazy-seq需要使用rest进行遍历,而不建议使用next。
问题类表:
1.with-open 为什么不能执行第2个表达式?
2.lazy-seq 使用什么方式读取比较好?
懒序列是 Clojure 的关键特征之一。它的使用原则是:尽可能让序列保持为懒序列,直到真正需要结果的时候。
不鼓励用懒序列来包含副作用的操作。良好的代码风格,最好用 doseq
来代替 map
, for
等序列操作符。
clojure
(doseq [line (line-seq rdr)] (tran-b64 f))
让代码可读性更高,将副作用明确出来,也可以在 doseq
中增加新的副作用操作,例如 (println f)
。
如果非要用 map
操作来执行,应该是将 dorun
放在整个懒序列外面,而不是强制将line-seq
实化:
clojure
(dorun (map (partial ran-b64 f) (line-seq rdr)))
懒序列实质上是延迟执行的程序,以数据的外表存在,是“程序即数据”的极好例子。对它的最佳使用方法,是将操作(最好是无副作用的代码)用序列函数处理后重新成为数据。仅仅在输入输出时引入副作用。因此:
clojure
(defn tran-b64 [s] (String. (b64/decode (.getBytes s)))) (defn gen-tmp-file [] "gen temp file" (str (System/getProperty "java.io.tmpdir") (System/getProperty "file.separator") (java.util.UUID/randomUUID) ".tmp")) (defn tran [filename x] (with-open [rdr (io/reader x) wtr (io/writer filename :append true)] (doseq [line (map tran-b64 (line-seq rdr))] (.write wtr line)))) (defn read-tran-file [] (let [filename (gen-tmp-file)] (tran filename "https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt")))
PS: f
在 clojure 程序中习惯表示为一个函数,因此我都改成了 filename
。read-tran-file
, gen-tmp-file
函数名都不恰当,不能反映其真正工作,read-tran-file
函数实际上写了文件,gen-tmp-file
实际上是 tmp-filename
。