Skip to content
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

lapply-sapply concept #285

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions concepts/lapply-sapply/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"authors": ["colinleach"],
"contributors": [],
"blurb": "lapply() and sapply() will apply an unvectorized function to a vector or list"
}
78 changes: 78 additions & 0 deletions concepts/lapply-sapply/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# About

We saw previously that many functions in R are "vectorized": they will operate across entire vectors, with no need to write explicit loops.

Functions using only vectorized functions are themselves likely to be automatically vectorized.
In a trivial example:

```R
> triple <- function(x) return(3 * x)
> triple(1:10)
[1] 3 6 9 12 15 18 21 24 27 30
```

However, more complex function bodies may not be fully vectorized, for example if they contain loops or if-else blocks.
Then we need a way to apply them to vectors (similar to `map()` in some other languages).

Because R has a rich variety of data structures in the core language, it also has a whole family of `*apply()` functions to operate on them.
For now, consider `lapply()` and `sapply()`.

## `lapply()`

"List-apply" is designed to work on list inputs, but vectors will be silently coerced to lists as necessary.

The output is a list, so this may need unpacking to get a vector:

```R
h <- function(size) {
switch(size,
"small" = 6,
"medium" = 8,
"large" = 10
)
}

> v <- c("small", "large", "medium")

> h(v) # bad use of an unvectorized function
Error in switch(size, small = 6, medium = 8, large = 10) :
EXPR must be a length 1 vector

> lapply(v, h) # output is a list
[[1]]
[1] 6

[[2]]
[1] 10

[[3]]
[1] 8

> unlist(lapply(v, h)) # output is a vector
[1] 6 10 8
```

## `sapply()`

"Simplified-apply" will do something similar, but in this case returns a named vector.
Depending on context, use of `unname()` may be necessary:

```R
> sapply(v, h)
small large medium
6 10 8

> unname(sapply(v, h))
[1] 6 10 8
```

## Advanced Topics: Alternative Approaches

Base R also has a `vapply()` function.
This is similar to `sapply()` but also has a required parameter to specify the output format.

Some developers prefer this extra level of predictability when writing library code.
Using it interactively or in simple functions is probably less useful.

Outside Exercism, there is also the `purrr` package, which provides several additional options such as `map()`.
Less constrained by backwards compatibility than base R, this package is currently closer to modern functional programming in other languages.
67 changes: 67 additions & 0 deletions concepts/lapply-sapply/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Introduction

We saw previously that many functions in R are "vectorized": they will operate across entire vectors, with no need to write explicit loops.

Functions using only vectorized functions are themselves likely to be automatically vectorized.
In a trivial example:

```R
> triple <- function(x) return(3 * x)
> triple(1:10)
[1] 3 6 9 12 15 18 21 24 27 30
```

However, more complex function bodies may not be fully vectorized, for example if they contain loops or if-else blocks.
Then we need a way to apply them to vectors (similar to `map()` in some other languages).

Because R has a rich variety of data structures in the core language, it also has a whole family of `*apply()` functions to operate on them.
For now, consider `lapply()` and `sapply()`.

## `lapply()`

"List-apply" is designed to work on list inputs, but vectors will be silently coerced to lists as necessary.

The output is a list, so this may need unpacking to get a vector:

```R
h <- function(size) {
switch(size,
"small" = 6,
"medium" = 8,
"large" = 10
)
}

> v <- c("small", "large", "medium")

> h(v) # bad use of an unvectorized function
Error in switch(size, small = 6, medium = 8, large = 10) :
EXPR must be a length 1 vector

> lapply(v, h) # output is a list
[[1]]
[1] 6

[[2]]
[1] 10

[[3]]
[1] 8

> unlist(lapply(v, h)) # output is a vector
[1] 6 10 8
```

## `sapply()`

"Simplified-apply" will do something similar, but returns a named vector.
Depending on context, use of `unname()` may be necessary:

```R
> sapply(v, h)
small large medium
6 10 8

> unname(sapply(v, h))
[1] 6 10 8
```
18 changes: 18 additions & 0 deletions concepts/lapply-sapply/links.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[
{
"url": "https://www.rdocumentation.org/packages/base/versions/3.6.2/topics/lapply",
"description": "lapply: Apply a Function over a List or Vector"
},
{
"url": "https://www.dataquest.io/blog/apply-functions-in-r-sapply-lapply-tapply/",
"description": "Apply Functions in R with Examples"
},
{
"url": "https://www.learnbyexample.org/r-apply-functions/",
"description": "R Apply Family"
},
{
"url": "https://adv-r.hadley.nz/functionals.html",
"description": "Advanced R: Functionals"
}
]
5 changes: 5 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,11 @@
"uuid": "2751b6f2-7d71-4397-b063-9bf927a57756",
"slug": "booleans",
"name": "Booleans"
},
{
"uuid": "137411b7-5af8-4a78-9225-5b85353ba81e",
"slug": "lapply-sapply",
"name": "Lapply Sapply"
}
],
"key_features": [
Expand Down