In general terms, list comprehensions should:
- be distinct from (nested) for loops and the use of map & filter functions within the syntax of the language.
- return either a list or an iterator (an iterating being something that returns successive members of a collection, in order),
In Clojure, list comprehension is via the
for function. This is different to the for in other langauges as you will see.
(for [number [1 2 3]] (* number 2))
for function should be read as follows:
"for each number in the collection [1 2 3], apply the function (* number 2)"
Couldnt we just do this with map? Yes, we could.
(map #(* % 2) [1 2 3])
So why to we need
for function? It really shows its value when you are working with multiple collections
(for [number [1 2 3] letter [:a :b :c]] (str number letter))
Again we could use
map function for this as follows
(mapcat (fn [number] (map (fn [letter] (str number letter)))))
So with the
for function we can do the same calculation with much easier code to reason about.
Filtering results with predicates
for funciton we can add a filter on the results by using a predicate, to test if a condition is true or false. Any values that meet the condition as true are returned, values that are false are ommitted.
(for [x (range 10) :when (odd? x)] x) (for [x (range 10) :while (even? x)] x)
To do this kind of filtering with maps would be possible, however the code would be more harder for humans to parse and understand.
Note Create a 3-tumbler combination padlock, with each tumbler having a range of 0 to 9. Count the number of possible combinations. Then add a predicate that filters out some of the combinations
Lets just model all the possible combinations
(for [tumbler-1 (range 10) tumbler-2 (range 10) tumbler-3 (range 10)] [tumbler-1 tumbler-2 tumbler-3])
Now lets count the combinations
(count (for [tumbler-1 (range 10) tumbler-2 (range 10) tumbler-3 (range 10)] [tumbler-1 tumbler-2 tumbler-3]))
Now add a predicate using
:when to filter out the combinations that do not match.
(count (for [tumbler-1 (range 10) tumbler-2 (range 10) tumbler-3 (range 10) :when (or (= tumbler-1 tumbler-2) (= tumbler-2 tumbler-3) (= tumbler-3 tumbler-1))] [tumbler-1 tumbler-2 tumbler-3]))
Note Create a 2 character prefix for tickets, using capital letters from the English alphabet. However, exclude I and O as they can be mistakend for numbers