Clojure and me has moved.

Wednesday, April 29, 2009

.toUpperCase

This post has moved, go to its new location
I recently learnt that when you want to convert the case of a technical identifier (a tagname, a HTTP header etc.) you must not use plain .toUpperCase or .toLowerCase but specify Locale/ENGLISH.

Tuesday, April 28, 2009

Counting occurences (solution to the exercise)

This post has moved, go to its new location
What does (merge-with + {:a 12} {:b 4} {:a 3 :b 7}) return?
{:b 11, :a 15} when there are several values for a key, these values are merged (two at a time) using the specified function — here they are summed.
Can you count occurrences of each value in a collection using merge-with?
(apply merge-with + (map (fn [x] {x 1}) coll)) or, using for: (apply merge-with + (for [x coll] {x 1})).

Monday, April 27, 2009

Screenscraping with Enlive

This post has moved, go to its new location
(select (html-resource (java.net.URL. "http://clojure-log.n01se.net/")) [:#main [:a (attr? :href)]]) returns a seq of link nodes.

Counting occurences

This post has moved, go to its new location
One asked me how to count occurrences of each value in a collection. I answered (reduce #(assoc %1 %2 (inc (%1 %2 0))) {} coll). Since it can take some time to get accustomed to the functional way of thought, here is how one can work such an expression out:
How to count all occurrences of 42?
(count (filter #{42} coll))
How to express count using reduce?
(defn my-count [coll] (reduce (fn [n _] (inc n)) 0 coll))
How to count all occurrences of 42 using reduce?
(reduce (fn [n _] (inc n)) 0 (filter #{42} coll))
Can you get rid of the filter?
(reduce (fn [n x] (if (= 42 x) (inc n) n)) 0 coll)
I'd like the result to be {42 occurences-count}.
(reduce (fn [m x] (if (= 42 x) (assoc m 42 (inc (m 42))) m)) {42 0} coll)
42 is hardcoded in four places, it's bad!
(reduce (fn [m x] (if (= 42 x) (assoc m x (inc (m x))) m)) {42 0} coll)
Can't you replace {42 0} with {}?
No (inc (m x)) would fail because (m x) would return nil.
How does one provide a default value when the key is not found ?
(a-map a-key default-value)
Can't you replace {42 0} with {}?
(reduce (fn [m x] (if (= 42 x) (assoc m x (inc (m x 0))) m)) {} coll)
I changed my mind I'd like you to count occurrences of each value.
Easy! (reduce (fn [m x] (assoc m x (inc (m x 0)))) {} coll) or, terser, (reduce #(assoc %1 %2 (inc (%1 %2 0))) {} coll)

Exercise:
What does (merge-with + {:a 12} {:b 4} {:a 3 :b 7}) return?
Can you count occurrences of each value in a collection using merge-with?

Wednesday, April 22, 2009

Vicious bug

This post has moved, go to its new location
What's wrong with this code (do (defmacro foobar [x] (doto x println)) (foobar (+ 1 1)))?
Nothing.
Run it again.
The first time, foobar is treated as a function (its argument has been evaluated) because foobar is unknown before the whole top-level expression is compiled.
The second time, the var named foobar preexists and is a macro, (foobar (+ 1 1)) is expanded.
This behaviour bit me while using with-test to test macros. It's the kind of bug that goes unnoticed while developing incrementally and evaluating in the same REPL, it was waiting for me to start a fresh REPL.

Sanitizing HTML with Enlive

This post has moved, go to its new location
net.cgrand.enlive-html=> (sniptest "<div id=user-data>" 
[:#user-data] (html-content "code injection<script>alert('boo')</script>")
[:#user-data (but #{:p :br :a :strong :em})] nil)
"<html><body><div id=\"user-data\">code injection</div></body></html>"
You also need to remove most attributes but it's just a demo of something that was impossible with the old Enlive.

By the way, the old Enlive is no more. Long live the new Enlive!

Saturday, April 18, 2009

Make it work, make it right, make it fast

This post has moved, go to its new location
Make it work, make it right, make it fast.
Make it work
sort of done: the old Enlive,
Make it right
work in progress, the new Enlive,
Make it fast
once the "right" branch is merged into master.

See the README file to know what's new and here for an example.

Friday, April 17, 2009

Mapping every second item

This post has moved, go to its new location
I wanted to apply a function to every second item in a coll. I was considering writing something using interleave, take-nth and map or a combination of mapcat and partition when I thought of this:
(map #(%1 %2) (cycle [f identity]) coll)
I really love clojure's map parallel processing. (I should ask if every? and some could be allowed to take several colls.)