我正在通过解决问题来学习 Clojure,我陷入困境 with one of them ,基本上我必须在日志文件中找到前五个字符串。
这是我到目前为止所得到的:
(ns topfive
(:import (java.io BufferedReader FileReader)))
(defn extract-query [line]
(.substring line (+ (.lastIndexOf line "=") 1) (.lastIndexOf line "]")))
(defn process-file [file-name, queries]
(with-open [rdr (BufferedReader. (FileReader. file-name))]
(doseq [line (line-seq rdr)]
(assoc queries (extract-query line) (inc (get queries (extract-query line) 0))))))
(process-file "in" {})
我的问题是 queries
不包含任何内容,我已经检查过 extract-queries
返回我想要的字符串,我认为这可能有问题与语言本身有关,I've read Clojure 在语言级别具有不变性,但这对我来说仍然不是一个好点。
你能对我做错的事情提出一些建议吗?
最佳答案
Clojure 确实具有低级别的不变性,并且 HashMap 是不可变的。因此,assoc 不会就地改变映射,它会创建一个包含更新项的新映射,并返回新映射。您一遍又一遍地调用 assoc
,但丢弃结果。
一个修复方法是使用 reduce
而不是 doseq
。 doseq
迭代 seq 并对每个项目执行某些操作,但不会累积任何结果。所以它应该主要用于有副作用的事情,例如打印到屏幕或文件。 reduce
类似地迭代 seq,但它确实会累积结果。
(defn process-file [file-name, queries]
(with-open [rdr (BufferedReader. (FileReader. file-name))]
(reduce (fn [queries, line]
(assoc queries (extract-query line) (inc (get queries (extract-query line) 0))))
queries
(line-seq rdr))))
您可以做一些事情来进一步简化这一点。 process-file
不需要 queries
参数,因为它一开始总是一个空映射。使用 update-in
和 fnil
可以更简洁地编写 assoc
行;这也让我们避免每行调用两次 extract-query
。您可以使用 clojure.java.io
中的 Clojure 包装器 reader
替换对 Java Reader 类的所有调用。您可以用正则表达式替换对 substring
的调用;正则表达式更简洁,但对于大量输入,您的版本可能执行得更快。您还可以使用 #()
将示例中的匿名函数替换为含糖阅读器宏版本,尽管此时它开始看起来有点吵,所以我可能会使用 let
使其读起来更好一点。
(ns topfive
(:require [clojure.java [io :as io]]))
(defn extract-query [line]
(nth (re-find #"query=([^]]+)" line) 1))
(defn process-file [file-name]
(with-open [rdr (io/reader file-name)]
(reduce #(let [search-term (extract-query %2)]
(update-in %1 [search-term] (fnil inc 0)))
{}
(line-seq rdr))))
关于dictionary - doseq 中的 assoc 字符串导致空映射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6418177/