## Introduction to Clojure - Part 2 ## Tools ## Topics

 Functions on collections High order functions Lazy sequences Loop/Recur ## Topics for later

• Destructuring
• multi-arity functions
• loop recur
• Walk/Prewalk
• Macros

## Functions on collections

Collections play a central role in `clojure`
The collections share a common interface. In `clojure`, collections are called sequences.
Usually, sequences are lazy.

## Sequences - interface

Here are a couple of functions available on sequences and here is the full interface for sequences

`count`

``(count (zipmap (range 100) (range 200))) ``

`seq` is useful when we need to test whether a sequence is empty

``````[(seq [1 2 3])
(seq [])]``````

We can take a few element - even from an infinite sequence:

``(take 10 (range))``

We can take elements while a predicate holds:

``(take-while (partial > 10) (range))``

We can also take the last elements of a list - but don't try that on an infinite list!

``(take-last 10 (range 100))``

Play also with `drop` `drop-last` and `drop-while`...

## Sequences - high order functions

map

`(map inc [1 2 3])`

filter

``(filter even? (range 10))``

remove

``(remove even? (range 10))``

reduce

``(reduce (fn [res x] (* res x)) 1 (range 1 11))``

## Map, filter and reduce

Compose a string from the letters whose ascii values are odd numbers

We are going to implement this in several ways, improving our code style at each stage:

1. The worst way:

``````(reduce (fn [res i]
(if (odd? i) (str res (char i)) res))
"" (range 97 123))``````

2. Giving each stage a name:

``````(let [lst (range 97 123)
f-lst (filter odd? lst)
m-lst (map char f-lst)]
(reduce str "" m-lst))``````
3. Using the `as->` threading macro

``````(identity (as-> (range 97 123) \$
(filter odd? \$)
(map char \$)
(reduce str "" \$)))``````
4. Using the `->>`threading macro:

``````(->> (range 97 123)
(filter odd?)
(map char)
(reduce str ""))``````

## Sequences - Practice

Write by yourslef the following funtions of `clojure.core`

Write a `clojure` version of `spark`'s function `reduceByKey`: receives a list of `(K, V)` pairs and a function `f` and returns a list of `(K, V)` pairs where the values for each key are aggregated using the given reduce function `f`

``````(defn reduce-by-key [f lst])
(and
(= (reduce-by-key '((a 1)) +) '((a 1)))
(= (reduce-by-key '((a 1) (a 3)) +) '((a 4)))
(= (reduce-by-key '((a 1) (a 3) (b 1) (a 5) (b 6)) +) '((a 9) (b 7))))``````

## High order functions

Function that receives function[s] and return a function ## High order functions

apply

``(apply + (range 10))``

comp

``((comp list inc) 9)``

You can call `apply` on `comp`. For instance:

``((apply comp [inc inc inc]) 3)``

partial

``````(def match-aa (partial re-matches #"aa"))
[(match-aa "aaaa")
(match-aa "aa")]``````

## High order functions (cont.)

memoize

First, a non-memoized function that print its arguments

When called twice with the same argument, it prints twice

``````(defn foo[x] (println "foo called with: " x))
(symbol (with-out-str
(foo 1)
(foo 1)))``````

With memoization, the function is called only once, the other calls are cached:

``````(def foo-memo (memoize foo))
(with-out-str
(foo-memo 1)
(foo-memo 1))``````

juxt

``((juxt inc dec (partial * 10) even?) 1)``

## High Order Functions - Practice

Write by yourslef the following funtions of `clojure.core`

comp (only with 2 functions)

partial (only supporting functions with 2 arguments)

comp with no limit on the number of functions

partial supporting functions any number of arguments

## Lazy Sequences

Elements are evaluated only when we really need them The sequences returned by a lot of `clojure` functions are lazy: `map`, `filter`, `concat`, `range`, `take`...

## Lazy Sequences - Examples

Infinite range of numbers:

``(take 10 (range))``

Infinite repetition of an element:

``(take 100 (repeat 1))``

Infinite repetition of a sequence:

``(take 100 (cycle [1 2 3]))``

Infinite repetition of a function call:

``(take 10 (repeatedly (partial rand-int 100)))``

## Lazy Sequences - creation

There is a handy macro `lazy-seq` that makes it easy to create lazy sequences. Let's see it in action, by writing code that receives a sequece and increment each of its elements - without using `map`:

First a non-lazy implementation - with a infinite recursion

``````(defn inc-seq [s]
(cons (inc (first s))
(inc-seq (rest s))))
(take 10 (inc-seq (range 100)))``````

Let's fix our code - by ending the recursion when the list is empty

``````(defn inc-seq [s]
(when (seq s)
(cons (inc (first s))
(inc-seq (rest s)))))
(take 10 (inc-seq (range 100)))``````

And now the lazy implementation:

``````(defn inc-seq-lazy [s]
(lazy-seq
(cons (inc (first s))
(inc-seq-lazy (rest s)))))
(take 10 (inc-seq-lazy (range 100)))``````

We can even pass an infinite lazy sequence to our function:

``(take 10 (inc-seq-lazy (range)))``

## Lazy sequences - Practice

Write a function that creates a infinite lazy sequence of the positive integers

First write a function `numbers-starting-at` that receives a number `n` and returns all the numbers starting at `n`

``````(defn numbers-starting-at [n] )
(= (take 3 (numbers-starting-at 5)) '(5 6 7))``````

Now write a function `numbers` that receive no arguments and return all the positive integers. Try to use `partial`

## Tail Call Recursion

Regular recursions are dangerous as they might cause stack overflow. In clojure, there is a semantic support for tail call recursion. At first, it's less intuitive...
But when you get used to it, it's very convenient...

## Tail Call Recursion - Basics

• Stack overflow with regular recursion

``````(defn count-recursive [s]
(if (empty? s) 0
(inc (count-recursive (rest s)))))

(count-recursive (range 10000))``````

Looks good right?
Increment the size of `s` up to 1000000 and see what happens.

• The solution is to use tail call recursion. Like this:

``````(defn count-tail-recursive [s]
(loop [s s res 0]
(if (empty? s) res
(recur (rest s) (inc res)))))

(count-tail-recursive (range 10000))``````

It works fine with 1000000 elements

• The hard thing when writing tail call recursion is that you are not allowed to do anything after the `recur`. Trying to do that will cause an exception at compile time.

``````(loop [s (range 100)]
(if (empty? s)
10
(inc (recur (rest s)))))``````
• Here are more detials about `recur` in clojure

## Tail Call Recursion - Practice

• Write a function that checks whether an element is present in a list

``(defn present? [lst x])``
• Write a function that sums up the elements of a sequence. The proper solution is to use `reduce`. But here, we want to practice `recur`

``(defn sum [lst])``
• Write a function that receives a seuence of sequences and returns their concatanation. The proper solution would be to use `(apply concat s)`; but here we want to practice `recur`

``(defn concat-seqs [seqs])``

## Congratulations Now, you are familiar with:

• Functions on collections
• High order functions
• Lazy sequences
• Tail call recursion

/