# ✓ Core Functional Programming¶

Core functional:

• Branching and conditional

• Sequences

• Iterations

• Looping

Sequence transformation

• Map

• Reduce

API

• Domain-specific functions and data structures

## Expressions¶

### (do …) form¶

The do-form allows us to evaluate multiple expressions in sequence, but only the last expression will be used as the value of the do-form.

(do (+ 1 2)
"Hello"
(str "The last " "expression."))

"The last expression."


### (let …) form¶

Did you know that the let-form also allows multiple expressions as part of its body. Just like the do-form, the last expression is used as the value of the let-form.

(let [x 1
y 2
z 3]
(str "x=" x)
(str "y=" y)
(str "z=" z))

"z=3"


### Function composition reviews¶

(defn square [x] (* x x))
(defn to-string [x] (str "x = " x))

#'user/to-string

;;
;; A deep nested function invocation
;;

(let [x 42]
(to-string (+ (inc (- (square x) 1000)) 2.4)))

"x = 767.4"


### The -> thread¶

(-> x
(f1 y1 y2 y3 ...)
(f2 z1 z2 z3 ...))


becomes

x ---+
|
|
(f1 ___ y1 y2 y3 ...) ----+
|
+--------------------+
|
|
(f2 ___ z1 z2 z3 ...)

;;
;; A different view of function composition as a pipeline
;;
(let [x 42]
(-> x
square
(- 1000)
(inc)
(+ 2.4)
(to-string)))

"x = 767.4"


### The ->> thread¶

(->> x
(f1 y1 y2 y3 ...)
(f2 z1 z2 z3 ...))


becomes

x ---------------+
|
|
(f1 y1 y2 y3 ... ___) ----+
|
+--------+
|
|
(f2 z1 z2 z3 ... ___)

(to-string (+ 2.4 (inc (- 1000 (square 42)))))

"x = -760.6"

(->> 42
(square)
(- 1000)
(inc)
(+ 2.4)
(to-string))

"x = -760.6"


### as-> threading¶

(to-string (+ 2.4 (inc (- (square 42) 1000))))

"x = 767.4"

(as-> 42 x
(square x)
(- x 1000)
(inc x)
(+ 2.4 x)
(to-string x))

"x = 767.4"


## Branching¶

### If-else¶

• True: true or anything that is not nil.

• False: false or nil.

(if (> 2 3)
"2 > 3"
"not 2 > 3")

"not 2 > 3"

(if (< 2 3)
"2 < 3"
"not 2 < 3")

"2 < 3"

(defn age-binary-category [age]
(if (< age 65)
"Not senior"
"Senior"))

#'user/age-binary-category

(age-binary-category 42)

"Not senior"


### If-else is not that great¶

(defn age-many-category [age]
(if (< age 4)
"toddler"
(if (< age 12)
"preteen"
(if (< age 21)
"teen"
(if (< age 50)
(if (< age 65)
"midlife"
"senior"))))))

#'user/age-many-category

(age-many-category 23)

"adult"

(age-many-category 13)

"teen"

(age-many-category 63)

"midlife"


### Cond form¶

(defn age-category [age]
(cond
(< age 4) "toddler"
(< age 12) "pretten"
(< age 21) "teen"
(< age 65) "midlife"
:else "senior"))

#'user/age-category

(age-category 23)

"adult"

(age-category 13)

"teen"


## Iterations¶

### Sequences: a first look¶

Lisp is all about processing lists. Clojure enriches lists with many variations which collectively are referred to as sequences.

• Eager sequences are sequences whose elements are created when the sequence is created.

• Lazy sequences are sequences whose elements are created only when they are needed.

### Creating sequences¶

(range 10)

(0 1 2 3 4 5 6 7 8 9)

(range 10 20)

(10 11 12 13 14 15 16 17 18 19)

(range 10 20 3)

(10 13 16 19)


Range sequences are lazy.

### Iteration with for-form¶

(for [i (range 5)]
(str "Iteration i:" i))

("Iteration i:0" "Iteration i:1" "Iteration i:2" "Iteration i:3" "Iteration i:4")


### Using for-form as data¶

(println (clojure.string/join "\n"
(for [i (range 5)]
(str "i=" i))))

i=0
i=1
i=2
i=3
i=4

nil

(as-> 5 $(range$)
(for [i $] (str "i=" i)) (clojure.string/join "\n"$)
(println \$))

i=0
i=1
i=2
i=3
i=4

nil


### for-loop with :let and :when clauses¶

(for [i (range 10)
:let [square (* i i)]
:when (even? i)]
(str "square of " i " is " square))

("square of 0 is 0" "square of 2 is 4" "square of 4 is 16" "square of 6 is 36" "square of 8 is 64")


### (for ...) form with multiple bindings¶

How many pairs (x, y) of integers are there between 0 to 100 such that:

• 16x + y/4 is between (50, 60)

• x and y are less than 10 apart

(defn is-solution? [x y] (<= 50 (+ (* 16 x) (/ y 4)) 60))
(defn close? [x y] (<= 0 (Math/abs (- x y)) 9))

#'user/close?

(let [solutions (for [x (range 100)
y (range 100)
:when (and (is-solution? x y)
(close? x y))]
[x y])]
(println "Solutions:" solutions)
(println "Count:" (count solutions)))

Solutions: ([3 8] [3 9] [3 10] [3 11] [3 12])
Count: 5

nil


### Iteration without results¶

(doseq [x (range 100)
y (range 100)
:when (and (is-solution? x y)
(close? x y))]
(println "(x, y) = " x y))

(x, y) =  3 8
(x, y) =  3 9
(x, y) =  3 10
(x, y) =  3 11
(x, y) =  3 12

nil


### Another variation of iteration without results¶

(dotimes [i 4]
(println i))

0
1
2
3

nil


## The most general iteration¶

### Loop and recur¶

(loop [ <initial-bindings> ]
<body>)


where the body must contain a recursion call of the form

(recur <new values...>)

(loop [count-down 5]
(if (pos? count-down)
(do (println count-down "...")
(recur (dec count-down)))
(println "Boom")))

5 ...
4 ...
3 ...
2 ...
1 ...
Boom

nil

(defn count-sequence [collection]
(loop [size 0
x collection]
(if (empty? x)
size
(recur (inc size) (rest x)))))

#'user/count-sequence

(count-sequence [:a :b :c])

3

(count-sequence "hello world")

11


## A Case Study¶

### Identify a prime number¶

(defn prime? [n]
(empty? (for [m (range 2 (dec n))
:when (zero? (mod n m))] m)))

#'user/prime?

(prime? 10)

false

(prime? 11)

true


### List prime numbers by an upper limit¶

(defn prime-numbers [limit]
(for [n (range 2 limit)
:when (prime? n)]
n))

#'user/prime-numbers

(prime-numbers 100)

(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97)


### List prime numbers by count¶

(defn all-prime-numbers []
(for [n (range)
:when (and (<= 2 n) (prime? n))]
n))

#'user/all-prime-numbers


First approach: use sequence transformation

(defn get-prime-numbers [first-n]
(take first-n (all-prime-numbers)))

#'user/get-prime-numbers

(get-prime-numbers 10)

(2 3 5 7 11 13 17 19 23 29)


Second approach: use the core loop/recur

(defn loop-prime-numbers [first-n]
(loop [result []
more-numbers (all-prime-numbers)]
(cond
(= first-n (count result)) result
:else (let [n (first more-numbers)]
(recur (conj result n)
(rest more-numbers))))))

#'user/loop-prime-numbers

(loop-prime-numbers 10)

[2 3 5 7 11 13 17 19 23 29]