| compose | R Documentation |
To compose functions,
Use compose():
compose(f, g, h, ...)
This makes the function that applies f, then g, then h, etc.
It has the formals of the first function applied (namely
f). For example, if
fun <- compose(paste, toupper)
then the function fun() has the same signature as paste(), and the call
fun(letters, collapse = ",")
is equivalent to the composite call
toupper(paste(letters, collapse = ","))
Use `%>>>%`:
f %>>>% g %>>>% h %>>>% ...
It comprehends both the semantics of the
magrittr `%>%`
operator and quasiquotation. For example, if
sep <- "" fun <- sample %>>>% paste(collapse = !!sep)
then the function fun() has the same signature as sample(), and the
call
fun(x, size, replace, prob)
is equivalent to the composite call
paste(sample(x, size, replace, prob), collapse = "")
Use as.list() to recover the list of composite functions. For example, both
as.list(compose(paste, capitalize = toupper)) as.list(paste %>>>% capitalize: toupper)
return the (named) list of functions list(paste, capitalize = toupper).
compose(...) fst %>>>% snd
... |
Functions or lists thereof to compose, in order of application.
Lists of functions are automatically spliced in.
Unquoting of names, via |
fst, snd |
Functions. These may be optionally named using a colon ( |
Function of class CompositeFunction, whose
formals are those of the first function applied (as a
closure).
The `%>>>%` operator adopts the semantics of the
magrittr `%>%`
operator:
Bare names are matched to functions: For example, in a composition like
... %>>>% foo %>>>% ...
the ‘foo’ is matched to the function of that name.
Function calls are interpreted as a unary function of a point (.):
A call is interpreted as a function (of a point) in one of two ways:
If the point matches an argument value, the call is literally interpreted as the body of the function. For example, in the compositions
... %>>>% foo(x, .) %>>>% ... ... %>>>% foo(x, y = .) %>>>% ...
the ‘foo(x, .)’, resp. ‘foo(x, y = .)’, is
interpreted as the function function(..., . = ..1) foo(x, .), resp.
function(..., . = ..1) foo(x, y = .).
Otherwise, the call is regarded as implicitly having the point as its first argument before being interpreted as the body of the function. For example, in the compositions
... %>>>% foo(x) %>>>% ... ... %>>>% foo(x, y(.)) %>>>% ...
the ‘foo(x)’, resp. ‘foo(x, y(.))’, is interpreted
as the function function(..., . = ..1) foo(., x), resp.
function(..., . = ..1) foo(., x, y(.)).
Expressions {...} are interpreted as a function of a point (.):
For example, in a composition
... %>>>% {
foo(.)
bar(.)
} %>>>% ...
the ‘{foo(.); bar(.)}’ is interpreted as the function
function(..., . = ..1) {foo(.); bar(.)}.
Curly braces are useful when you need to circumvent `%>>>%`'s
usual interpretation of function calls. For example, in a composition
... %>>>% {foo(x, y(.))} %>>>% ...
the ‘{foo(x, y(.))}’ is interpreted as the function
function(..., . = ..1) foo(x, y(.)). There is no point as first
argument to foo.
As a matter of convenience, some exceptions are made to the above interpretation of calls as functions:
Parenthesis (() applies grouping. (In R, `(` is indeed a
function.) In particular, expressions within parentheses are literally
interpreted.
Colon (:) applies naming, according to the syntax
‘<name>: <function>’, where ‘<function>’ is interpreted
according to the semantics of `%>>>%`. For example, in
... %>>>% aName: foo %>>>% ...
the function foo is named "aName".
fn(), namespace operators (`::` ,
`:::`) and extractors (`$`, `[[`,
`[`) are literally interpreted. This allows for list extractors to
be applied to composite functions appearing in a `%>>>%` call (see
'Operate on Composite Functions as List-Like Objects'). For example, the
compositions
paste %>>>% tolower paste %>>>% base::tolower (paste %>>>% toupper)[[1]] %>>>% tolower
are equivalent functions.
The `%>>>%` operator supports Tidyverse
unquoting (via !!). Use it to:
Enforce immutability: For example, by unquoting res in
res <- "result" get_result <- identity %>>>% lapply(`[[`, !!res)
you ensure that the function get_result() always extracts the component
named "result", even if the binding res changes its value or is
removed altogether.
Interpret the point (.) in the lexical scope: Even though
`%>>>%` interprets ‘.’ as a function argument, you can
still reference an object of that name via unquoting. For example,
. <- "point"
is_point <- identity %>>>% {. == !!.}
determines a function that checks for equality with the string "point".
Name composite functions, programmatically: For example, unquoting
nm in
nm <- "aName" ... %>>>% !!nm: foo %>>>% ...
names the ‘foo’-component of the resulting composite function
"aName".
Accelerate functions by fixing constant dependencies: For example,
presuming the value of the call f() is constant and that g is a
pure function (meaning that its return value depends only on its
input), both
... %>>>% g(f()) %>>>% ... ... %>>>% g(!!f()) %>>>% ...
would be functions yielding the same values. But the first would compute
f() anew with each call, whereas the second would simply depend on a
fixed, pre-computed value of f().
You can think of a composite function as embodying the (possibly nested) structure of its list of constituent functions. In fact, you can apply familiar index and assignment operations to a composite function, as if it were this list, getting a function in return. This enables you to leverage composite functions as structured computations.
For instance, the ‘sum’ in the following composite function
f <- abs %>>>% out: (log %>>>% agg: sum)
can be extracted in the usual ways:
f[[2]][[2]]
f[[c(2, 2)]]
f$out$agg
f[["out"]][["agg"]]
f[["out"]]$agg
f$out[[2]]
f[[list("out", 2)]]
The last form of indexing with a mixed list is handy when you need to create an index programmatically.
Additionally, you can excise sub-composite functions with
[, head(), tail(). For example:
Both f[1] and head(f, 1) get the ‘abs’ as a composite
function, namely compose(abs)
f[2:1] reverses the order of the top-level functions to yield
out: (log %>>>% agg: sum) %>>>% abs
f$out[c(FALSE, TRUE)] gets the ‘sum’ as a (named) composite
function
Similarily, subset assignment works as it does for lists. For instance, you
can replace the ‘sum’ with the identity function:
f[[2]][[2]] <- identity
f$out$agg <- identity
f[["out"]][["agg"]] <- identity
f$out[[2]] <- identity
f[[list("out", 2)]] <- identity
Multiple constituent functions can be reassigned using
[<-. For example
f[2] <- list(log) f["out"] <- list(log) f[c(FALSE, TRUE)] <- list(log)
all replace the second constituent function with log, so that f becomes
abs %>>>% log.
The generic methods unlist(), length(), names() also apply to
composite functions. In conjunction with compose(), you can use
unlist() to “flatten” compositions. For example
compose(unlist(f, use.names = FALSE))
gives a function that is identical to
abs %>>>% log %>>>% sum
The speed of a composite function made by compose() or `%>>>%`
(regardless of its nested depth) is on par with a manually constructed
serial composition. This is because compose() and `%>>>%` are
associative, semantically and operationally. For instance, triple
compositions,
compose(f, g, h) f %>>>% g %>>>% h compose(f, compose(g, h)) f %>>>% (g %>>>% h) compose(compose(f, g), h) (f %>>>% g) %>>>% h
are all implemented as the same function. Lists of functions are automatically “flattened” when composed.
Nevertheless, the original nested structure of constituent functions is
faithfully recovered by as.list(). In particular, as.list() and
compose() are mutually invertible: as.list(compose(fs)) is the same
as fs, when fs is a (nested) list of functions. (But note that the
names of the list of composite functions is always a character vector; it
is never NULL.)
constant(); combined with `%>>>%`, this provides a lazy,
structured alternative to the
magrittr `%>%`
operator.
# Functions are applied in the order in which they are listed
inv <- partial(`/`, 1) # reciprocal
f0 <- compose(abs, log, inv)
stopifnot(all.equal(f0(-2), 1 / log(abs(-2))))
# Alternatively, compose using the `%>>>%` operator
f1 <- abs %>>>% log %>>>% {1 / .}
stopifnot(all.equal(f1(-2), f0(-2)))
## Not run:
# Transform a function to a JSON function
library(jsonlite)
# By composing higher-order functions:
jsonify <- {fromJSON %>>>% .} %>>>% {. %>>>% toJSON}
# By directly composing with input/output transformers:
jsonify <- fn(f ~ fromJSON %>>>% f %>>>% toJSON)
## End(Not run)
# Formals of initial function are preserved
add <- function(a, b = 0) a + b
stopifnot(identical(formals(compose(add, inv)), formals(add)))
# Compositions can be provided by lists, in several equivalent ways
f2 <- compose(list(abs, log, inv))
f3 <- compose(!!! list(abs, log, inv))
f4 <- compose(abs, list(log, inv))
f5 <- compose(abs, !!! list(log, inv))
stopifnot(
all.equal(f2, f0), all.equal(f2(-2), f0(-2)),
all.equal(f3, f0), all.equal(f3(-2), f0(-2)),
all.equal(f4, f0), all.equal(f4(-2), f0(-2)),
all.equal(f5, f0), all.equal(f5(-2), f0(-2))
)
# compose() and as.list() are mutally invertible
f6 <- compose(abs, as.list(compose(log, inv)))
stopifnot(
all.equal(f6, f0), all.equal(f6(-2), f0(-2))
)
fs <- list(abs, log, inv)
stopifnot(all.equal(check.attributes = FALSE,
as.list(compose(fs)), fs,
))
# `%>>>%` supports names, magrittr `%>%` semantics, and quasiquotation
sep <- ""
scramble <- shuffle: sample %>>>% paste(collapse = !!sep)
nonsense <- scramble(letters)
stopifnot(
nchar(nonsense) == 26L,
identical(letters, sort(strsplit(nonsense, sep)[[1]])),
identical(scramble$shuffle, sample)
)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.