It's the end of the week and I have to pace myself. The next chapter in Clojure for the Brave and True is about macros, which I don't want to spend too much time on it.
I also got stomped on the pascal triangle problem from 4clojure yesterday so one of my goals today is to solve it, my own solution to it.
A macro is created and used like this:
(defmacro infix
"Use this macro when you pine for the notation of your childhood"
[infixed]
(list (second infixed) (first infixed) (last infixed)))
(infix (1 + 1))
; => 2```
The key difference between the macro and a function is that the function arguments are fully evaluated before they're passed to the function. Macro arguments are unevaluated data. When writing a macro, you will most likely have to quote symbols to avoid having them evaluated.
### Quoting
You use quoting to obtain an evaluated symbol. `(quote (+ 1 2))` will return `(+ 1 2)`. Quoting a symbol returns the symbol regardless if it has values or not.
Syntax quoting is done by prepending ```. It will always return the namespace on top of the symbol itself. Here is the difference between quoting and syntax quoting:
```clojure
'(+ 1 2)
; => (+ 1 2)
`(+ 1 2)
; => (clojure.core/+ 1 2)```
With syntax quoting you can unquote things, so you can do:
```clojure
`(+ 1 ~(inc 1))
; => (clojure.core/+ 1 2)
So after the tilde, the expression is evaluated and replaced by 2. In a way it's similar to string interpolation (string interpolation is done like this Hello #{name}
). The same way, you prepend a tilde and it evaluates in the list. Here are two equivalent ways to do the same thing:
(list '+ 1 (inc 1))
; => (+ 1 2)
`(+ 1 ~(inc 1))
; => (clojure.core/+ 1 2)
- variable capture happens when the macro hijacks the symbol with a let.
- double evaluation is when a form passed to a macro gets evaluated twice
This one was an epic problem to solve. Took me some time, and sweat. In the end the iterate solution was much easier: it runs a function over and over on the result of the previous run. Then you can just keep the last element in the vector. So to do this, first I wrote the next row function which takes the previous one, sums the elements in pair (I discovered that map
can take multiple collections and run the a function f with the nth element from each collection -- which is great!), and then we just tack a 1
at the beginning and one at the end too.
Now we have the list of rows (it goes on forever so we cap it with take
-- at n-1
), Since with iterate we have to start somewhere we start with [1 1] (because if we use rest
and butlast
we end up with empty collections to sum with one another).
(fn pascal [n]
(if (= n 1)
[1]
(last (take (dec n) (iterate (fn [xs]
(concat
(conj (map + (butlast xs) (rest xs)) 1)
'(1)))
[1 1])))))
The chapter on macros wasn't too useful to me at this point. I am knowingly pushing this back to a later time. I did read most of it and will be getting back to it but after ClojureFam.
I am happy about completing the pascal triangle problem though. In the end it wasn't that complicated. I just needed to find a way to tackle this. I first started with a recursion, which didn't prove easy after all and iterate
proved to be a must better choice.
Let's end the day with a tally of what I completed:
- Chapter 8 of Clojure for the Brave and True
- One 4clojure problems (the Pascal triangle problem!)