Skip to content

Add map and reduce concepts #595

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
4 changes: 4 additions & 0 deletions concepts/map/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"blurb": "The map function applies a given function to each element of one or more collections.",
"authors": ["JaumeAmoresDS"]
}
49 changes: 49 additions & 0 deletions concepts/map/about.md
Original file line number Diff line number Diff line change
@@ -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))
```

49 changes: 49 additions & 0 deletions concepts/map/introduction.md
Original file line number Diff line number Diff line change
@@ -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))
```

6 changes: 6 additions & 0 deletions concepts/map/links.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[
{
"url": "https://clojuredocs.org/clojure.core/map",
"description": "ClojureDocs: map"
}
]
4 changes: 4 additions & 0 deletions concepts/reduce/.meta/config.json
Original file line number Diff line number Diff line change
@@ -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"]
}
99 changes: 99 additions & 0 deletions concepts/reduce/about.md
Original file line number Diff line number Diff line change
@@ -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.

99 changes: 99 additions & 0 deletions concepts/reduce/introduction.md
Original file line number Diff line number Diff line change
@@ -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.

6 changes: 6 additions & 0 deletions concepts/reduce/links.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[
{
"url": "https://clojuredocs.org/clojure.core/reduce",
"description": "ClojureDocs: reduce"
}
]
10 changes: 10 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down