Hi, Thanks for your review and your improvements (of course also to Justin, whose improvements are also useful). I'll try to merge them into some "optimal" solution :)
On Tue, 21 Dec 2010 20:46:56 -0800 (PST) Benny Tsai <benny.t...@gmail.com> wrote: > 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. Thats very neat. I need to think about it a bit to merge this with Justins code, which only does one regexp matching per line. > '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} Oh, fmap looks useful, good to know. I 'd probably use a map and convert it back instead, but this is useful. > - (first (first ...)) can be shortened into (ffirst ...) Yeah, I'd usually use `caar' here :) > - 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. Oh, I didn't know about remove, that's nice. > - 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. It does, but doesn't that make it less lazy? To reverse something, it needs to evaluate the whole sequence. I yet have to learn how to deal with lazyness. > - In the final 'doseq', (first item) and (second item) can be replaced > by destructuring. Great! I was wondering whether Clojure supports something like tuple-unpacking in Python. Does it also support "patterned" destructuring like: first, *middle, last = sequence(...) -or- first, rest = sequence(...) The latter could be achived by something like `first+rest', I suppose, but don't know the "Clojure" name for it. > Hope you find this useful :) -- 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