在doseq中关联字符串会导致空映射

Alb*_*gni 3 clojure map

我正在通过解决问题来学习Clojure,我坚持使用其中一个,基本上我必须在日志文件中找到前五个字符串.

这是我到目前为止所得到的:

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

我的问题是queries不包含任何东西,我已经检查过它extract-queries返回我想要的字符串,我认为这可能与语言本身有关,我已经读过 Clojure在语言层面具有不变性,但这仍然对我来说似乎不是一个好点.

你能就我的错误提出一些建议吗?

Bri*_*per 9

Clojure确实具有较低级别的不变性,并且哈希映射是不可变的.因此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))))
Run Code Online (Sandbox Code Playgroud)

您可以做一些事情来进一步简化这一点.不需要queries参数process-file,因为它始终是一个空的地图.您assoc可以使用update-in和更简洁地编写您的专线fnil; 这也让我们避免extract-query每行调用两次.您可以替换所有的Java读取器类与Clojure的包装呼叫readerclojure.java.io.您可以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))))
Run Code Online (Sandbox Code Playgroud)

  • 人们还可以利用内置函数:`(frequency(map extract-query(line-seq rdr)))` (5认同)