2011-06-20 25 views
3

我正在通過解決問題來學習Clojure,我被卡住了with one of them,基本上我必須在日誌文件中找到前五個字符串。在劑量q中產生相關字符串導致空圖

這裏是我到目前爲止有:

(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在語言層面上具有不變性,但對我來說這仍然不是一個好的觀點。

你能提出一些關於我在做什麼的錯嗎?

回答

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,因爲它始終是一個空的地圖。您的assoc行可以使用update-infnil更簡潔地編寫;這也讓我們避免每行兩次撥打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)))) 
+5

人們還可以利用內置的功能:'(頻率(圖提取查詢(線-SEQ RDR) ))' –

+0

非常感謝Brian,你提供了很多值得思考的有趣點。 @Justin:我會嘗試的,也要感謝你。 –

+0

@Justin:謝謝,我會看看那個。 –

1

除了Brians出色答卷:該線程宏可能提高可讀性:

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

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