Skip to content

Latest commit

 

History

History
100 lines (70 loc) · 3.76 KB

2020-07-05.md

File metadata and controls

100 lines (70 loc) · 3.76 KB

Learning Clojure in Public - Week 2 Day 7 (14/35)

Expectations

This is a rest day after the holiday, I expect to do as much as I can of Chapter 4 of Brave Clojure without setting expectations too high.

What I learned

Programming by Abstractions

Programming by abstractions means that a certain function is not tailored to a specific data structure. It is about the operation, not about the underlying datastructure.

For example, the battery abstraction includes the operation “connect a conducting medium to its anode and cathode,” and the operation’s output is electrical current. It doesn’t matter if the battery is made out of lithium or out of potatoes. It’s a battery as long as it responds to the set of operations that define battery.

In Clojure, if cons, first and rest work on a datastructure then it's a seq and any seq funcion will work on it. Potentially it is because these functions are implemented with cons, first and rest.

So I tried to re-implement map in terms of these three functions:

(defn my-map
  [f coll]
  (if (nil? coll)
    nil
    (cons (f (first coll) (map (rest coll))))))

After testing it out, I looked up the code, and indeed, map is made with these three functions: https://github.com/clojure/clojure/blob/clojure-1.10.1/src/clj/clojure/core.clj#L2727

More about seq functions

map

  • It is possible to pass more than one sequence to map.
  • You can also pass it a collection of functions, here is a great example:
(def sum #(reduce + %))
(def avg #(/ (sum %) (count %)))
(defn stats
  [numbers]
  (map #(% numbers) [sum count avg]))

(stats [3 4 10])
; => (17 3 17/3)

(stats [80 1 44 13 6])
; => (144 5 144/5)
  • Or to retrieve the the value associated with a keyword. Again here is an example from Brave Clojure:
(def identities
  [{:alias "Batman" :real "Bruce Wayne"}
   {:alias "Spider-Man" :real "Peter Parker"}
   {:alias "Santa" :real "Your mom"}
   {:alias "Easter Bunny" :real "Your dad"}])

(map :real identities)
; => ("Bruce Wayne" "Peter Parker" "Your mom" "Your dad")

reduce

  • It's possible to use reduce to transform a map's value (same keys but updated values):
(reduce (fn [new-map [key val]]
          (assoc new-map key (inc val)))
        {}
        {:max 30 :min 10})
; => {:max 31, :min 11}```
- You can also use `reduce` to filter out some values in a map:
```clojure
(reduce (fn [new-map [key val]]
          (if (> val 4)
            (assoc new-map key val)
            new-map))
        {}
        {:human 4.1
         :critter 3.9})
; => {:human 4.1}

take and drop

  • Both take a sequence and a number and will return either the first n elements of the sequence (for the former), or everything but the first n elements (for the latter).
  • There is also take-while and drop-while that will take a predicate function instead of the number. However, in this case I would normally use filter. The advantage of take-while and drop-while is that it doesn't process all your data. It stops when the predicate is not satisfied anymore.

filter and some

You can get some to return the actual value of a function if you transform the predicate function by wrapping it in an and like this: (some #(and (> (:critter %) 3) %) food-journal).

sort and sort-by

sort-by takes a function to sort, so you could use count for instance

Takeaways

Today I read and took notes for a little more than half of the 4th Chapter of Clojure for the Brave and True. It goes further than Clojure for the Ground Up on the same topics. Going through it now makes complete sense as I have the basics down and I can pick up why things are the way they are, and I hope, why Clojure makes sense.