(ns wide-finder
"A basic map/reduce approach to the wide finder using agents.
Optimized for being idiomatic and readable rather than speed."
(:use [clojure.contrib.duck-streams :only [reader]]))
(def re #"GET /(\d+) ")
(defn inc-or-init [i]
(if i (inc i) 1))
(defn count-line
"Increment the relevant entry in the counts map."
[counts line]
(if-let [[_ hit] (re-find re line)]
(update-in counts [hit] inc-or-init)
counts))
(defn find-widely
"Return a map of pages to hit counts in filename."
[filename n]
(let [agents (map agent (repeat n {}))]
(dorun (map #(send %1 count-line %2)
(cycle agents) (line-seq (reader filename))))
(doseq [a agents] (await a))
(apply merge-with + (map deref agents))))