diff --git a/concepts/map/.meta/config.json b/concepts/map/.meta/config.json new file mode 100644 index 000000000..908de6fdc --- /dev/null +++ b/concepts/map/.meta/config.json @@ -0,0 +1,4 @@ +{ + "blurb": "The map function applies a given function to each element of one or more collections.", + "authors": ["JaumeAmoresDS"] + } diff --git a/concepts/map/about.md b/concepts/map/about.md new file mode 100644 index 000000000..4479bdab1 --- /dev/null +++ b/concepts/map/about.md @@ -0,0 +1,49 @@ +# Introduction + +In its most basic form, the higher-order function `map` accepts two arguments: a function `f` and a collection `coll`. + +Given `f` and `coll`, `map` applies `f` to each element of `coll`, returning a list of the results in the same order. + +```clojure +(map inc [1 2 3]) ; => (2 3 4) +``` + +The previous example applies the function `inc` to each element of the vector and returns the result as the list `(2 3 4)`. + +Let's see another example where we greet someone 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` does not return a list but a *lazy* sequence. + +This essentially means that the elements of the resulting sequence are not generated until they are actually needed. + +In other words, the function `f` passed to `map` is not applied until the elements of the resulting sequence are requested, for example, by printing them or retrieving their values. + +This is advantageous when `f` is computationally expensive and only some elements are actually needed. + +## Multiple collections + +`map` allows multiple collections to be passed as input. + +If the number of collections passed is `n`, the function `f` will receive `n` arguments, one from each collection. + +The result is a sequence where the i-th element is obtained by applying `f` to the `n` elements at position i from 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 000000000..4479bdab1 --- /dev/null +++ b/concepts/map/introduction.md @@ -0,0 +1,49 @@ +# Introduction + +In its most basic form, the higher-order function `map` accepts two arguments: a function `f` and a collection `coll`. + +Given `f` and `coll`, `map` applies `f` to each element of `coll`, returning a list of the results in the same order. + +```clojure +(map inc [1 2 3]) ; => (2 3 4) +``` + +The previous example applies the function `inc` to each element of the vector and returns the result as the list `(2 3 4)`. + +Let's see another example where we greet someone 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` does not return a list but a *lazy* sequence. + +This essentially means that the elements of the resulting sequence are not generated until they are actually needed. + +In other words, the function `f` passed to `map` is not applied until the elements of the resulting sequence are requested, for example, by printing them or retrieving their values. + +This is advantageous when `f` is computationally expensive and only some elements are actually needed. + +## Multiple collections + +`map` allows multiple collections to be passed as input. + +If the number of collections passed is `n`, the function `f` will receive `n` arguments, one from each collection. + +The result is a sequence where the i-th element is obtained by applying `f` to the `n` elements at position i from 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/links.json b/concepts/map/links.json new file mode 100644 index 000000000..7bcefa9bf --- /dev/null +++ b/concepts/map/links.json @@ -0,0 +1,6 @@ +[ + { + "url": "https://clojuredocs.org/clojure.core/map", + "description": "ClojureDocs: map" + } +] diff --git a/concepts/reduce/.meta/config.json b/concepts/reduce/.meta/config.json new file mode 100644 index 000000000..a4f65382b --- /dev/null +++ b/concepts/reduce/.meta/config.json @@ -0,0 +1,4 @@ +{ + "blurb": "The reduce function combines all elements of a collection into a single value by applying a function cumulatively", + "authors": ["JaumeAmoresDS"] + } diff --git a/concepts/reduce/about.md b/concepts/reduce/about.md new file mode 100644 index 000000000..414d20426 --- /dev/null +++ b/concepts/reduce/about.md @@ -0,0 +1,99 @@ +# Introduction + +The higher-order funcion `reduce` accepts either two or three arguments. + +Let's see each one in turn. + +## Reduce with three arguments + +When called with three arguments, `(reduce f val coll)` runs sequentially as follows: + +- First, it 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 +``` + +## Reduce with two arguments + +When called with two arguments, `(reduce f coll)` runs sequentially as follows: + +- First, it applies the function `f` to the two first elements `x_1` and `x_2` of the collection `coll`. +- Then, it 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)`: + +- The function `f` applies the first function `g_1` from the collection to `val` +- Then applies 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 000000000..414d20426 --- /dev/null +++ b/concepts/reduce/introduction.md @@ -0,0 +1,99 @@ +# Introduction + +The higher-order funcion `reduce` accepts either two or three arguments. + +Let's see each one in turn. + +## Reduce with three arguments + +When called with three arguments, `(reduce f val coll)` runs sequentially as follows: + +- First, it 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 +``` + +## Reduce with two arguments + +When called with two arguments, `(reduce f coll)` runs sequentially as follows: + +- First, it applies the function `f` to the two first elements `x_1` and `x_2` of the collection `coll`. +- Then, it 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)`: + +- The function `f` applies the first function `g_1` from the collection to `val` +- Then applies 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/links.json b/concepts/reduce/links.json new file mode 100644 index 000000000..5dbca2e07 --- /dev/null +++ b/concepts/reduce/links.json @@ -0,0 +1,6 @@ +[ + { + "url": "https://clojuredocs.org/clojure.core/reduce", + "description": "ClojureDocs: reduce" + } +] diff --git a/config.json b/config.json index 698bace5c..e816d37b0 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": [