Hi Marek, Here's my tweaked version:
(ns karma (:use [clojure.contrib.duck-streams :only (read-lines)]) (:use [clojure.contrib.generic.functor :only (fmap)])) (def allowed-nickname "[A-z]{1,16}") (def upvote-regexp (re-pattern (format "(%s)\\+\\+" allowed- nickname))) (def downvote-regexp (re-pattern (format "(%s)\\-\\-" allowed- nickname))) (defn get-votes [regexp line] (let [nicks (map second (re-seq regexp line))] (frequencies nicks))) (defn get-histogram [line] (let [upvotes (get-votes upvote-regexp line) downvotes (fmap - (get-votes downvote-regexp line))] (merge-with + upvotes downvotes))) (defn -main [& args] (let [file-name (ffirst args) histograms (map get-histogram (read-lines file-name)) histogram (apply merge-with + histograms) non-zero-histogram (remove (comp zero? second) histogram) sorted-by-karma (reverse (sort-by second non-zero-histogram))] (doseq [[nick karma] sorted-by-karma] (println nick karma)))) (-main *command-line-args*) The only non-trivial change is in the way the histogram is created. Instead of "updating" an accumulator map while processing each line, I create a mini-histogram for each line and then merge them together at the end. 'get-votes' is a lot like 'extract-nicks', but leverages the built-in 'frequencies' fn to build a histogram of the upvotes/downvotes in a line, depending on the regexp passed in: karma=> (get-votes upvote-regexp "c++ d++ b++ c--") {"c" 1, "d" 1, "b" 1} karma=> (get-votes downvote-regexp "c++ d++ b++ c--") {"c" 1} Given a line, 'get-histogram' uses 'get-votes' to get a histogram of the upvotes and a histogram of the downvotes. It then uses 'fmap' to convert the downvotes into negative numbers: karma=> (fmap - {"c" 1}) {"c" -1} 'get-histogram' then uses 'merge-with' to combine the two histograms into a histogram for the line: karma=> (merge-with + {"c" 1, "d" 1, "b" 1} {"c" -1}) {"c" 0, "d" 1, "b" 1} In '-main', 'merge-with' is used again to combine all the histograms into a single histogram. After that, the code is essentially identical to the original, except for a few small tweaks: - (first (first ...)) can be shortened into (ffirst ...) - In cases like removing nicks with 0 karma, instead of (filter (not pred) ...), I prefer (remove pred ...); I find it a little easier to follow that way. - To sort the nicks by karma in descending order, instead of sorting by the negation of the karma, I used (reverse (sort-by ...)); again, just a subjective thing, makes the intent more clear to me. - In the final 'doseq', (first item) and (second item) can be replaced by destructuring. Hope you find this useful :) On Dec 21, 6:38 pm, Marek Kubica <ma...@xivilization.net> wrote: > Hi, > > I wrote a small log file analyzer for IRC logs. We use nickname++ and > nickname-- to track the "karma", so after trying to write it in > JavaScript (failed due to to the fact that Gjs/Seed are unmature yet), > Factor (failed because I am just too stupid to understand it), Guile > (failed because I ran into encoding/regex-problems), so I gave Clojure > a try. > > I really liked the immutable data stuctures, so I tried to avoid > mutation. > > So uhm, this is my first Clojure program. I tend to learn a lot from > code reviews and would be happy if someone could take a look at it and > tell me what to improve and how to write more idiomatic code. > > Code is here, and reproduced below: > <https://github.com/Leonidas-from-XIV/karmawhore/blob/68ff6b681c5862fa...> > > Thanks in advance! > > regards, > Marek > > (ns net.xivilization.karmawhore > (:gen-class) > (:use [clojure.contrib.duck-streams :only (read-lines)])) > > (def allowed-nickname "[A-z]{1,16}") > (def nick-plus (re-pattern (format "(%s)\\+\\+" allowed-nickname))) > (def nick-minus (re-pattern (format "(%s)\\-\\-" allowed-nickname))) > > (defn extract-nicks [regexp line] > (map second (re-seq regexp line))) > > (defn modify-karma [op h nick] > (let [current-value (h nick)] > (if (nil? current-value) (assoc h nick (op 1)) > (assoc h nick (op current-value 1))))) > > (def increase-karma (partial modify-karma +)) > (def decrease-karma (partial modify-karma -)) > > (defn process-line [acc line] > (let [nicks-add (extract-nicks nick-plus line) > nicks-sub (extract-nicks nick-minus line) > after-add (reduce increase-karma acc nicks-add)] > (reduce decrease-karma after-add nicks-sub))) > > (defn -main [& args] > (let [file-name (first (first args)) > histogram (reduce process-line (hash-map) (read-lines > file-name)) nonzero? (comp not zero? second) > histogram (filter nonzero? histogram) > sorted-by-karma (sort-by #(- (second %)) histogram)] > (doseq [item sorted-by-karma] > (printf "%s %d\n" (first item) (second item))))) > > (-main *command-line-args*) -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en