lapply()
is a really useful function but it has some limitations. For example, with lapply()
there is no way to:
- add new elements
- remove existing elements
- change the names of list elements
To overcome these problems we could create a variant of lapply()
than I'm going to call fapply()
(where f
is for flexible). Rather than calling the supplied function with with an element of the list extract with [[
, it will call it with a sub-list extracted with [
.
fapply <- function(x, f, ...) {
out <- vector("list", length(x))
for (i in seq_along(x)) {
res <- f(x[i], ...)
stopifnot(is.list(x) && is.vector(x))
out[[i]] <- res
}
unlist(out, recursive = FALSE)
}
We can then use fapply()
to:
-
Change names:
x <- list(a = 1, b = 2, c = 3) fapply(x, function(x) setNames(x, toupper(names(x))))
-
Add new elements to the list:
x <- list(a = 1, b = 2, c = 3) fapply(x, function(x) rep(x, x[[1]]))
-
Remove elements from the list:
x <- list(a = 1, b = 2, c = 3) fapply(x, function(x) if (x[[1]] > 2) x)
The difference between lapply()
and fapply()
is a lot like the difference bewteen [[
and [
. In lapply()
, f
takes an element of a list and returns a new element; in fapply()
, f
takes a sublist and returns a sublist. An important property of fapply()
is the type of object that f
takes as input and returns as output is the same as the original x
, a list.
Monads are a generalisation of this idea. If you have an object, like a list, that provides a function that works like fapply()
, then it's a monad. It turns out that you can implement fapply()
in terms of some simpler functions, which are what monads are usually defined in terms of.