I just uploaded a library that builds on zippers but shifts emphasis from nodes to insertion-points (interstitial positions before, between and after nodes).
It eases growing trees from an empty root.
; show-ip represents the currently edited structure with * denoting the insertion-point
(defn show-ip [ip] (-> ip (insert-left '*) first z/root))
(def e (-> [] z/vector-zip (insertion-point :append)))
(-> e show-ip) ; [*]
(-> e (insert-left 1) show-ip) ; [1 *]
(-> e (insert-left 1) (insert-right 2) show-ip) ; [1 * 2]
(-> e (insert-left 1) (insert-right 2) left show-ip) ; [* 1 2]
(-> e (insert-left [1 2]) show-ip) ; [[1 2] *]
(-> e (insert-left [1 2]) left show-ip) ; [* [1 2]]
(-> e (insert-left [1 2]) left right show-ip) ; [[1 2] *]
(-> e (insert-left [1 2]) left next show-ip) ; [[* 1 2]]
(-> e (insert-left [1 2]) left next next show-ip) ; [[1 * 2]]
(-> e (insert-left [1 2]) left next next next show-ip) ; [[1 2 *]]
(-> e (insert-left [1 2]) left next next next next show-ip) ; [[1 2] *]
(-> e (insert-left [1 2]) left next next next next prev show-ip) ; [[1 2 *]]
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.
- 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})).
(select (html-resource (java.net.URL. "http://clojure-log.n01se.net/")) [:#main [:a (attr? :href)]]) returns a seq of link nodes.
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?
- 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.
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!
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.
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.)
I needed to update a seq as follows: apply a given function to items at specified indices and insert the result (a seq) in place.
My first laborious attempt involved extracting subseqs of untouched items, mapping the function on the remaining items then joining everything back. Bleh! (Really, I don’t like indices but this time I haven’t found a way to work around.)
I was sad when, suddenly, I found this shortest solution:
(defn mapsplice [f coll indices]
(let [need-splice (map (set indices) (iterate inc 0))]
(mapcat #(if %2 (f %1) [%1]) coll need-splice)))
;; (mapsplice #(repeat % %) (range 10) [7 3])
;; (0 1 2 3 3 3 4 5 6 7 7 7 7 7 7 7 8 9)