Another day, more Clojure concepts. This is day 3 of learning Clojure in public, with Athens' ClojureFam. I'm changing the the format of these posts and will be using the one I saw in my cohort-mate Mani's repository, learn-clojure-in-public, so here we go!
Yesterday I was a little bit disappointed with my performance on 4Clojure, it took me a lot longer to complete 7 problems than the 20 I did on Monday. Difficulty goes up, so that's expected. I also don't know how to use map
and reduce
in Clojure so I reached for recursion every single time. So today I decided to focus on Clojure From the Ground Up. Luckily Chapter 4 covers reduce & co. as well as recursion.
My initial expectation was to complete Chapter 4 and 5. However, after chatting with my cohort-mate I found out that Chapter 5 (which covers macros) can easily be skipped: it's complicated, not really necessary to contribute to Athens and I can easily do it at a later date. I would rather solidify my learning
Clojure from the Ground Up -- Chapter 4
The first part of this chapter was introducing recursions which was nice to go over again, but so far I have the opposite problem: I use recursions all the time in lisps. I was happy to cover the if
function though.
This chapter mostly felt like a laundry list of functions to manipulate sequences. I added them all to my function cheatsheet and it didn't really feel necessary to document this here, it would be redundant. I did learn some new things though:
For instance that strings are sequences too! You can break a string into a sequence of characters with seq
and rebuild it with apply
and str
like so (apply str (reverse "woolf"))
.
I was also quite impressed with some function, which I feel I have built myself many times over and are already there in Clojure. I found out that you can leverage quite niche tools from Java as well. If you want to figure out if a letter is uppercase or not you can call #(Character/isUpperCase %)
.
Among the functions that I was happy to see, were:
frequencies
will count how many times an element appears in a sequence.(frequencies [:meow :mrrrow :meow :meow])
will return{:meow 3, :mrrrow 1}
group-by
group sequences by a function, for instance
user=> (pprint (group-by :first [{:first "Li" :last "Zhou"}
{:first "Sarah" :last "Lee"}
{:first "Sarah" :last "Dunn"}
{:first "Li" :last "O'Toole"}])){"Li" [{:last "Zhou", :first "Li"} {:last "O'Toole", :first "Li"}],
"Sarah" [{:last "Lee", :first "Sarah"} {:last "Dunn", :first "Sarah"}]}
- And of course the famed
reduce
! It takes a function and will run it on the first two elements (unless you pass a starting value) and then run again on the result of that function and the next value.(reduce f default-value coll)
At the end of the chapter, the book walks you through building a function to find the sum of the products of consecutive pairs of the first 1000 odd integers. Here is my answer with a lot of help from the book:
(reduce +
(take 1000
(map (fn [pair] (* (first pair) (second pair)))
(partition 2 1 (filter odd? (iterate inc 0))))))
Every sequence is a collection, but not every collection is a sequence.
The seq
function makes it possible to convert a collection into a sequence. Any object supporting first
and rest
functions is a sequence.
Chapter 4 of Clojure From the Ground Up is the first one that comes with exercises. Here are my answers and choices for the first three. I decided to skip the prime number question because there's probably better use of my clojure learning time.
Write a function to find out if a string is a palindrome–that is, if it looks the same forwards and backwards.
The first idea that I get here is always to use a recursion. I know I won't have any issue so I looked for another solution. An easy one that is almost cheating is reversing the string and making sure it matches:
(defn palindrome? [word]
(= word (apply str (reverse word))))
I can find of a few solutions for this one. How about using frequencies
that I liked so much when I read about it in the chapter?
(defn countc [string]
(last (first
(filter (fn [pair] (= (first pair) \c))
(frequencies string)))))
Another easy way would be to keep the c's only and the count the members of the sequence:
(defn countc [string]
(count
(filter (fn [l] (= l \c))
(seq string))))
(defn my-filter [f xs]
(reduce
(fn [v e]
(if (f e) (conj v e) v))
[] xs))
(my-filter pos? [1 2 -1 1 0 1 -1])
Today I did 16 4clojure problems which brings my total number of complete problems to 43! I have added my answers to the 4clojure document.
Even though I decided to skip Chapter 5 for now and didn't match my expectations, I am glad I did. I have taken in a lot information in the past few days. I wouldn't want to overload myself with too much, and time spent applying this new knowledge is probably better spent. It's not a sprint, but a marathon! I will make sure over the next few days to spend some time to review and make flash cards. This will most likely be tomorrow and over the weekend when I will either have less time, or will be away from a computer.
Today has been the most active day for our ClojureFam cohort: we've been exchanging tips, solutions and encouragement. I hope this continues like this.
Let's end the day with a tally of what I completed:
- Chapter 4 of Clojure From the Group Up
- 3 problems from that chapter
- 16 4clojure problems