связанные строки в dosq приводят к пустой карте - PullRequest
3 голосов
/ 21 июня 2011

Я изучаю 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" {})

Моя проблема в том, что queries ничего не содержит, я уже проверил, что extract-queries возвращает нужную мне строку, я подумал, что это может иметь какое-то отношение к самому языку, У меня есть прочитайте , что Clojure имеет неизменность на уровне языка, но это все еще не кажется мне хорошим.

Не могли бы вы подсказать, что я делаю не так?

Ответы [ 2 ]

9 голосов
/ 21 июня 2011

Clojure имеет неизменность на низком уровне, а хэш-карты неизменны. Таким образом, assoc не изменяет карту на месте, она создает новую карту с обновленным элементом в ней и возвращает новую карту. Вы звоните assoc снова и снова, но отбрасываете результаты.

Одно исправление - использовать reduce вместо doseq. doseq перебирает последовательность и что-то делает с каждым элементом, но не накапливает никаких результатов. Поэтому его следует использовать в основном для вещей, которые имеют побочные эффекты, например печать на экран или в файл. 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-in и fnil; это также позволяет нам избежать вызова extract-query дважды на строку. Вы можете заменить все вызовы классов Java Reader оболочкой Clojure reader в clojure.java.io. Вы можете заменить свои звонки на substring регулярным выражением; regex более лаконичен, но для больших входов ваша версия может работать быстрее. Вы также можете заменить анонимную функцию в моем примере макро-версией сахарного читателя, используя #(), хотя в этот момент она начинает выглядеть немного шумно, поэтому я бы, вероятно, использовал 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 голос
/ 22 июня 2011

в дополнение к отличному ответу Brians: макрос потоков может улучшить читаемость:

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

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