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))))