Skip to content

Latest commit

 

History

History
81 lines (57 loc) · 3.71 KB

2020-07-10.md

File metadata and controls

81 lines (57 loc) · 3.71 KB

Learning Clojure in Public - Week 3 Day 5 (19/35)

Expectations

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.

What I learned

Macros

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)

Things to watch out for with macros

  • 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

Pascal's triangle 4clojure problem

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])))))

Takeaways

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!)