diff --git a/concepts/map/.meta/config.json b/concepts/map/.meta/config.json new file mode 100644 index 00000000..846390a9 --- /dev/null +++ b/concepts/map/.meta/config.json @@ -0,0 +1,4 @@ +{ + "blurb": "We provide here a basic explanation about the map higher-order function and its most common usage.", + "authors": ["JaumeAmoresDS"] + } \ No newline at end of file diff --git a/concepts/map/about.md b/concepts/map/about.md new file mode 100644 index 00000000..b1b9a151 --- /dev/null +++ b/concepts/map/about.md @@ -0,0 +1,35 @@ +# Introduction + +In its most basic form, the higher-order function `map` accepts two arguments: a function `f` and a sequence of elements `s`. It then applies `f` to each element of the sequence, and returns a list where the i-th element is the result of applying `f` to the i-th element of `s`: + +```clojure +(map inc [1 2 3]) ; => (2 3 4) +``` + +The previous example applies function `inc`, which increases a number by one, to each element of the vector `[1 2 3]`, and returns the result of this application as a list `(2 3 4)`, where each element is the result of applying `inc` to the corresponding element of [1 2 3]. + +Let's see another example where we greet a given person with the message "Welcome": + +```clojure +(defn say-welcome + [name] + (str "Welcome " name "!")) + +(map say-welcome ["Chris" "Jane" "Peter"]) ; => ("Welcome Chris!" "Welcome Jane!" "Welcome Peter!") +``` + +## Returning a lazy sequence + +Previously we provided a simplified explanation of how `map` operates. In reality, `map` returns a so-called *lazy* sequence. Although an explanation of lazy sequences goes beyond the scope of this introduction, it basically means that the elements of the resulting list are not generated until they are actually needed. In other words, it is not until we actually use elements of this list (e.g., to print them or retrieve their value) that the function `f` passed to `map` is applied to generate each element. This is advantageous when `f` is computationally expensive and we end-up only needing to retrieve some of the elements of the resulting list later in the program. + +## Using multiple collections + +`map` allows multiple collections to be passed as input. If the number of collections we pass is `n`, the function `f` will receive `n` elements, one for each collection in the list. The result is a list where the i-th element is obtained by applying `f` to the i-th element of each collection: + +```clojure +(def coll_a [a1 a2]) +(def coll_b [b1 b2]) +(def coll_c [c1 c2]) +(map f coll_a coll_b coll_c) ; => ((f a1 b1 c1) (f a2 b2 c2)) +``` + diff --git a/concepts/map/introduction.md b/concepts/map/introduction.md new file mode 100644 index 00000000..aec5b1ec --- /dev/null +++ b/concepts/map/introduction.md @@ -0,0 +1,19 @@ +# Introduction + +In its most basic form, the higher-order function `map` accepts two arguments: a function `f` and a sequence of elements `s`. It then applies `f` to each element of the sequence, and returns a list where the i-th element is the result of applying `f` to the i-th element of `s`: + +```clojure +(map inc [1 2 3]) ; => (2 3 4) +``` + +The previous example applies function `inc`, which increases a number by one, to each element of the vector `[1 2 3]`, and returns the result of this application as a list `(2 3 4)`, where each element is the result of applying `inc` to the corresponding element of [1 2 3]. + +Let's see another example where we greet a given person with the message "Welcome": + +```clojure +(defn say-welcome + [name] + (str "Welcome " name "!")) + +(map say-welcome ["Chris" "Jane" "Peter"]) ; => ("Welcome Chris!" "Welcome Jane!" "Welcome Peter!") +``` \ No newline at end of file diff --git a/concepts/map/links.json b/concepts/map/links.json new file mode 100644 index 00000000..9395ad83 --- /dev/null +++ b/concepts/map/links.json @@ -0,0 +1,10 @@ +[ + { + "url": "https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/map", + "description": "Clojure API: map" + }, + { + "url": "https://clojure.org/guides/higher_order_functions", + "description": "Clojure guides: Higher Order Functions" + } +] \ No newline at end of file diff --git a/concepts/reduce/.meta/config.json b/concepts/reduce/.meta/config.json new file mode 100644 index 00000000..4dd0dbc9 --- /dev/null +++ b/concepts/reduce/.meta/config.json @@ -0,0 +1,4 @@ +{ + "blurb": "We provide here a basic explanation about the reduce higher-order function and its most common usage.", + "authors": ["JaumeAmoresDS"] + } \ No newline at end of file diff --git a/concepts/reduce/about.md b/concepts/reduce/about.md new file mode 100644 index 00000000..eafbf24b --- /dev/null +++ b/concepts/reduce/about.md @@ -0,0 +1,57 @@ +# Introduction + +The higher-order funcion `reduce` accepts either two or three arguments. When called with three arguments, `(reduce f val coll)` applies the function `f` to `val` and the first element `x_1` of the collection `coll`: `(f val x_1)`. Then, it applies the function `f` to its own result and the second element `x_2` of the collection `coll`: `(f (f val x_1) x_2)`. Then again, it applies `f` to its previous result and the third element of `coll`, and so on until all the elements of `coll` are used. Let's see a typical example using `+` as our function: + +```clojure +(reduce + 100 [1 2 3 4]) +;=> (+ 100 1) +;=> (+ 101 2) +;=> (+ 103 3) +;=> (+ 106 4) +;=> 110 +``` + +When called with only two arguments, `(reduce f coll)` applies the function `f` to the two first elements `x_1` and `x_2` of the collection `coll`, then applies `f` to its own result and the third element `x_3` of `coll`, then again to its own result and the fourth element of `coll`, and so on until all the elements of `coll` are used: + +```clojure +(reduce + [1 2 3 4]) +;=> (+ 1 2) +;=> (+ 3 3) +;=> (+ 6 4) +;=> 10 +``` + +In all these cases, the function `f` must accept either two arguments or a variable number of arguments as happens in the previous case with function `+`. Let's see an example with a user-defined function: + +```clojure +(defn include-if-even + [coll x] + (if (= (mod x 2) 0) + (conj coll x) + coll)) +``` + +In the previous example, the function `include-if-even` accepts two arguments: a collection `coll` and a number `x`. It then checks if `x` module 2 is 0, in which case `x` is an even number. If that's the case, it includes this number into the collection `coll` and returns this collection. Otherwise, it returns the original collection. We can then apply `reduce` to collect all the even numbers from a given collection as follows: + +```clojure +(reduce include-if-even [] [1 2 3 4]) +;=> (include-if-even [] 1) +;=> (include-if-even [] 2) +;=> (include-if-even [2] 3) +;=> (include-if-even [2] 4) +;=> [2 4] +``` + +Note that in the previous example we must necessarily pass three arguments to `reduce`: our function `include-if-even`, an initial collection `[]`, and a collection of numbers like `[1 2 3 4]`. The initial collection `[]` is necessary due to the fact that `include-if-even` needs it as its first argument. + +## Especial cases + +Especial cases arise when we use an empty collection or a collection with only one item: + +- If we apply `reduce` to an empty collection, `(reduce f val coll)` returns `(f val)`, and `(reduce f coll)` returns the result of applying `f` with no arguments. In this case, the function `f` must be able to use no arguments. +- If we apply `reduce` to a collection with only one item, `(reduce f val coll)` returns `(f val x_1)`, i.e., the result of applying `f` to `val` and the first element `x_1` of `coll`. If used with only two arguments, `(f coll)` returns the only element found in `coll`, and `f` is not called. + +## Using collections of functions + +`reduce` accepts any type of collection, including one that contains functions. In that case, we will typically use three arguments `(reduce f val coll)`, and the function `f` applies the first function `g_1` from the collection to `val`, then the second function `g_2` to the result of the previous application, `(f (f val g_1) g_2)` and so on until all the functions are applied. + diff --git a/concepts/reduce/introduction.md b/concepts/reduce/introduction.md new file mode 100644 index 00000000..82318ea5 --- /dev/null +++ b/concepts/reduce/introduction.md @@ -0,0 +1,45 @@ +# Introduction + +The higher-order funcion `reduce` accepts either two or three arguments. When called with three arguments, `(reduce f val coll)` applies the function `f` to `val` and the first element `x_1` of the collection `coll`: `(f val x_1)`. Then, it applies the function `f` to its own result and the second element `x_2` of the collection `coll`: `(f (f val x_1) x_2)`. Then again, it applies `f` to its previous result and the third element of `coll`, and so on until all the elements of `coll` are used. Let's see a typical example using `+` as our function: + +```clojure +(reduce + 100 [1 2 3 4]) +;=> (+ 100 1) +;=> (+ 101 2) +;=> (+ 103 3) +;=> (+ 106 4) +;=> 110 +``` + +When called with only two arguments, `(reduce f coll)` applies the function `f` to the two first elements `x_1` and `x_2` of the collection `coll`, then applies `f` to its own result and the third element `x_3` of `coll`, then again to its own result and the fourth element of `coll`, and so on until all the elements of `coll` are used: + +```clojure +(reduce + [1 2 3 4]) +;=> (+ 1 2) +;=> (+ 3 3) +;=> (+ 6 4) +;=> 10 +``` + +In all these cases, the function `f` must accept either two arguments or a variable number of arguments as happens in the previous case with function `+`. Let's see an example with a user-defined function: + +```clojure +(defn include-if-even + [coll x] + (if (= (mod x 2) 0) + (conj coll x) + coll)) +``` + +In the previous example, the function `include-if-even` accepts two arguments: a collection `coll` and a number `x`. It then checks if `x` module 2 is 0, in which case `x` is an even number. If that's the case, it includes this number into the collection `coll` and returns this collection. Otherwise, it returns the original collection. We can then apply `reduce` to collect all the even numbers from a given collection as follows: + +```clojure +(reduce include-if-even [] [1 2 3 4]) +;=> (include-if-even [] 1) +;=> (include-if-even [] 2) +;=> (include-if-even [2] 3) +;=> (include-if-even [2] 4) +;=> [2 4] +``` + +Note that in the previous example we must necessarily pass three arguments to `reduce`: our function `include-if-even`, an initial collection `[]`, and a collection of numbers like `[1 2 3 4]`. The initial collection `[]` is necessary due to the fact that `include-if-even` needs it as its first argument. \ No newline at end of file diff --git a/concepts/reduce/links.json b/concepts/reduce/links.json new file mode 100644 index 00000000..be49344d --- /dev/null +++ b/concepts/reduce/links.json @@ -0,0 +1,26 @@ +[ + { + "url": "https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/partial", + "description": "Clojure API: partial" + }, + { + "url": "https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/comp", + "description": "Clojure API: comp" + }, + { + "url": "https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/comp", + "description": "Clojure API: comp" + }, + { + "url": "https://clojuredocs.org/clojure.core/memoize", + "description": "Clojure docs: memoize" + }, + { + "url": "https://clojuredocs.org/clojure.core/juxt", + "description": "Clojure docs: juxt" + }, + { + "url": "https://clojure.org/guides/higher_order_functions", + "description": "Clojure guides: Higher Order Functions" + } +] \ No newline at end of file diff --git a/config.json b/config.json index 698bace5..e816d37b 100644 --- a/config.json +++ b/config.json @@ -1092,6 +1092,16 @@ "uuid": "9d9d7cf1-8504-4f7b-b2a0-1b3b638dc0d9", "slug": "functions-generating-functions", "name": "Functions generating functions" + }, + { + "uuid": "2369e86f-6921-49b6-807d-b1bdf9e37cd2", + "slug": "map", + "name": "map" + }, + { + "uuid": "1fcc926b-4e79-4377-874f-41626e59b87b", + "slug": "reduce", + "name": "reduce" } ], "key_features": [