![]()  | 
    
                    
  | 
                
                     
                 | 
    
Collections play a central role in clojure
        The collections share a common interface.
        
         In clojure, collections are called sequences. 
        Usually, sequences are lazy. 
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...
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))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:
The worst way:
(reduce (fn [res i]
               (if (odd? i) (str res (char i)) res))
             "" (range 97 123))
                Why is it bad?
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))
            Using the as->  threading macro
(identity (as-> (range 97 123) $
                  (filter odd? $)
                  (map char $)
                  (reduce str "" $)))
            Using the ->>threading macro:
(->> (range 97 123)
                 (filter odd?)
                 (map char)
                 (reduce str ""))
            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))))
        
        
        Function that receives function[s] and return a function
    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")]
        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)
        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
        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...
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)))
        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)))
        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
        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...
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
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])
            
        Now, you are familiar with:
/