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

时间:2011-06-20 22:11:29

标签: map clojure

我正在通过解决问题来学习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返回我想要的字符串,我认为这可能与语言本身有关,{{ 3)} Clojure在语言层面具有不变性,但这对我来说似乎并不是一个好点。

你能否就我做错的事情提出一些建议?

2 个答案:

答案 0 :(得分:9)

Clojure确实具有较低级别的不变性,并且哈希映射是不可变的。因此assoc不会就地改变地图,它会创建一个包含更新项的新地图,并返回新地图。您一遍又一遍地呼叫assoc,但丢弃结果。

一种解决方法是使用reduce代替doseqdoseq遍历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))))

你可以做一些事情来进一步简化这一点。 queries参数不需要process-file,因为它始终是一个空的地图。您可以使用assocupdate-in更简洁地撰写fnil行。这也让我们避免每行调用extract-query两次。您可以使用reader中的Clojure包装器clojure.java.io替换对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))))

答案 1 :(得分:1)

除了Brians的优秀答案:线程宏可能会提高可读性:

(ns stackoverflow
  (:use [clojure.string :only [split]]
        [clojure.java.io  :only [reader]]))

(->> (reader "input.txt")
     (line-seq)
     (map #(last (split % #"=")))
     (frequencies))