English |  Español |  Français |  Italiano |  Português |  Русский |  Shqip

Developing a Clojure Edge

Sequences

Clojure has an excellent sequence abstraction which fits naturally into the language.  From a vector [1 2 3 4] we can find the odd numbers by calling the filter function:

(filter odd? [1 2 3 4])

-> (1 3)

We called the filter function with two arguments; the odd? function and a vector of integers.  filter is a higher order function, it takes an input function to use in its computation.  The result is a sequence of odd values.  Functions like filter that operate on sequences call seq on their arguments to convert collections to sequences.  The underlying mechanism is the ISeq interface which allows many collection data structures to provide access to their elements.


map is a function which calls another function for every element in a sequence:

(map inc [1 2 3 4])

-> (2 3 4 5)

The result is a sequence of the increment of each number in [1 2 3 4].


Sequences can be used as input arguments to other functions:

(filter odd? (map inc [1 2 3 4]))

-> (3 5)

We filtered by odd? the values from (2 3 4 5) which was the result of calling map.


To aggregate across a sequence, use reduce:

(reduce * [1 2 3 4])

-> 24

For each element in the sequence reduce computes (* aggregate element) and passes the result of that as the aggregate for the next calculation.  The first element 1 is used as the initial value of aggregate.  The final result is 1 * 2 * 3 * 4.


Grouped aggregates:

(group-by count ["the" "quick" "brown" "fox"])

-> {3 ["the" "fox"], 5 ["quick" "brown"]}

3 letter words are “the” and “fox”.

5 letter words are “quick” and “brown”.


Sequence abstractions are like names for loops which you can add to your vocabulary to talk about and recognize different kinds of loops.  Learning of the names of the abstractions and patterns that replace loops is an effort, but it adds powerful words to a programmer's vocabulary.  A large vocabulary facilitates reasoning more succinctly, communicating more effectively, and writing less code that does more.


The special form #() creates an anonymous function

#(< % 3)

The % symbol is an implied input argument.  This function takes one argument and returns true if the input argument is less than 3, otherwise false.  Anonymous functions are handy for adding small snippets of logic:

(filter #(< % 3) [1 2 3 4 5]))

-> (0 1 2)

Keeps only numbers less than 3.

(map #(if (odd? %) "odd" "even") [1 2 3 4 5])

-> ("odd" "even" "odd" "even" "odd")

Creates sequence of odd/even strings for each number in the vector.


Sequence abstractions are more concise and descriptive than loops, especially when filtering multiple conditions, or performing multiple operations.


Clojure also has useful functions for constructing sequences:

(range 5)

-> (0 1 2 3 4)

(repeat 3 1)

-> (1 1 1)

(partition 3 (range 9))

-> ((0 1 2) (3 4 5) (6 7 8))

There has been error in communication with Booktype server. Not sure right now where is the problem.

You should refresh this page.