lift_xy()
is a composition helper. It helps you compose
functions by lifting their domain from a kind of input to another
kind. The domain can be changed from and to a list (l), a vector
(v) and dots (d). For example, lift_ld(fun)
transforms a
function taking a list to a function taking dots.
lift(..f, ..., .unnamed = FALSE)
lift_dl(..f, ..., .unnamed = FALSE)
lift_dv(..f, ..., .unnamed = FALSE)
lift_vl(..f, ..., .type)
lift_vd(..f, ..., .type)
lift_ld(..f, ...)
lift_lv(..f, ...)
..f
. These will be
evaluated only once, when the lifting factory is called.TRUE
, ld
or lv
will not
name the parameters in the lifted function signature. This
prevents matching of arguments by name and match by position
instead.typeof()
, or "numeric" as a shorthand for either
"double" or "integer".do.call()
, so instead
of taking multiple arguments, it takes a single named list or
vector which will be interpreted as its arguments. This is
particularly useful when you want to pass a row of a data frame
or a list to a function and don't want to manually pull it apart
in your function.c(1, "2")
produces a character
vector) and will produce an error if the types are not
compatible. Additionally, you can enforce a particular vector
type by supplying .type
.lift_ld()
turns a function that takes a list into a
function that takes dots. lift_vd()
does the same with a
function that takes an atomic vector. These factory functions are
the inverse operations of lift_dl()
and lift_dv()
. lift_vd()
internally coerces the inputs of ..f
to
an atomic vector. The details of this coercion can be controlled
with .type
.lift_dl()
because it allows you to transform a regular function to one that
takes a list. This is often essential for composition with purrr
functional tools. Since this is such a common function,
lift()
is provided as an alias for that operation.
invoke()
### Lifting from ... to list(...) or c(...)
x <- list(x = c(1:100, NA, 1000), na.rm = TRUE, trim = 0.9)
lift_dl(mean)(x)
# Or in a pipe:
mean %>% lift_dl() %>% invoke(x)
# You can also use the lift() alias for this common operation:
lift(mean)(x)
# Default arguments can also be specified directly in lift_dl()
list(c(1:100, NA, 1000)) %>% lift_dl(mean, na.rm = TRUE)()
# lift_dl() and lift_ld() are inverse of each other.
# Here we transform sum() so that it takes a list
fun <- sum %>% lift_dl()
fun(list(3, NA, 4, na.rm = TRUE))
# Now we transform it back to a variadic function
fun2 <- fun %>% lift_ld()
fun2(3, NA, 4, na.rm = TRUE)
# It can sometimes be useful to make sure the lifted function's
# signature has no named parameters, as would be the case for a
# function taking only dots. The lifted function will take a list
# or vector but will not match its arguments to the names of the
# input. For instance, if you give a data frame as input to your
# lifted function, the names of the columns are probably not
# related to the function signature and should be discarded.
lifted_identical <- lift_dl(identical, .unnamed = TRUE)
mtcars[c(1, 1)] %>% lifted_identical()
mtcars[c(1, 2)] %>% lifted_identical()
#
### Lifting from c(...) to list(...) or ...
# Some functions such as mean() take an atomic vector. It is often
# useful to transform them to functions taking a list. In the
# following example, we lift mean() to apply it to each row of a
# data frame. This works because a row is essentially a list of
# length-1 vectors:
mtcars %>% by_row(lift_vl(mean))
# In other situations we need the vector-valued function to take a
# variable number of arguments as with pmap(). This is a job for
# lift_vd():
pmap(mtcars, lift_vd(mean))
# lift_vd() will collect the arguments and concatenate them to a
# vector before passing them to ..f. You can add a check to assert
# the type of vector you expect:
lift_vd(tolower, .type = character(1))("this", "is", "ok")
#
### Lifting from list(...) to c(...) or ...
# cross_n() normally takes a list of elements and returns their
# cartesian product. By lifting it you can supply the arguments as
# if it was a function taking dots:
cross <- lift_ld(cross_n)
out1 <- cross_n(list(a = 1:2, b = c("a", "b", "c")))
out2 <- cross(a = 1:2, b = c("a", "b", "c"))
identical(out1, out2)
# This kind of lifting is sometimes needed for function
# composition. An example would be to use pmap() with a function
# that takes a list. In the following, we use some() on each row of
# a data frame to check they each contain at least one element
# satisfying a condition:
mtcars %>% pmap(lift_ld(some, partial(`<`, 200)))
# Default arguments for ..f can be specified in the call to
# lift_ld()
lift_ld(cross_n, .filter = `==`)(1:3, 1:3) %>% str()
# Here is another function taking a list and that we can update to
# take a vector:
glue <- function(l) {
if (!is.list(l)) stop("not a list")
l %>% invoke(paste, .)
}
## Not run:
# letters %>% glue() # fails because glue() expects a list## End(Not run)
letters %>% lift_lv(glue)() # succeeds
Run the code above in your browser using DataLab