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.