R/reduce.R
reduce.Rd
reduce()
is an operation that combines the elements of a vector
into a single value. The combination is driven by .f
, a binary
function that takes two values and returns a single value: reducing
f
over 1:3
computes the value f(f(1, 2), 3)
.
reduce(.x, .f, ..., .init, .dir = c("forward", "backward")) reduce2(.x, .y, .f, ..., .init)
.x | A list or atomic vector. |
---|---|
.f | For For The reduction terminates early if |
... | Additional arguments passed on to the mapped function. |
.init | If supplied, will be used as the first value to start
the accumulation, rather than using |
.dir | The direction of reduction as a string, one of
|
.y | For |
When .f
is an associative operation like +
or c()
, the
direction of reduction does not matter. For instance, reducing the
vector 1:3
with the binary function +
computes the sum ((1 + 2) + 3)
from the left, and the same sum (1 + (2 + 3))
from the
right.
In other cases, the direction has important consequences on the
reduced value. For instance, reducing a vector with list()
from
the left produces a left-leaning nested list (or tree), while
reducing list()
from the right produces a right-leaning list.
reduce_right()
is soft-deprecated as of purrr 0.3.0. Please use
the .dir
argument of reduce()
instead. Note that the algorithm
has changed. Whereas reduce_right()
computed f(f(3, 2), 1)
,
reduce(.dir = \"backward\")
computes f(1, f(2, 3))
. This is the
standard way of reducing from the right.
To update your code with the same reduction as reduce_right()
,
simply reverse your vector and use a left reduction:
# Before: reduce_right(1:3, f) # After: reduce(rev(1:3), f)
reduce2_right()
is soft-deprecated as of purrr 0.3.0 without
replacement. It is not clear what algorithmic properties should a
right reduction have in this case. Please reach out if you know
about a use case for a right reduction with a ternary function.
accumulate()
for a version that returns all intermediate
values of the reduction.
# Reducing `+` computes the sum of a vector while reducing `*` # computes the product: 1:3 %>% reduce(`+`)#> [1] 61:10 %>% reduce(`*`)#> [1] 3628800# When the operation is associative, the direction of reduction # does not matter: reduce(1:4, `+`)#> [1] 10reduce(1:4, `+`, .dir = "backward")#> [1] 10# However with non-associative operations, the reduced value will # be different as a function of the direction. For instance, # `list()` will create left-leaning lists when reducing from the # right, and right-leaning lists otherwise: str(reduce(1:4, list))#> List of 2 #> $ :List of 2 #> ..$ :List of 2 #> .. ..$ : int 1 #> .. ..$ : int 2 #> ..$ : int 3 #> $ : int 4#> List of 2 #> $ : int 1 #> $ :List of 2 #> ..$ : int 2 #> ..$ :List of 2 #> .. ..$ : int 3 #> .. ..$ : int 4# reduce2() takes a ternary function and a second vector that is # one element smaller than the first vector: paste2 <- function(x, y, sep = ".") paste(x, y, sep = sep) letters[1:4] %>% reduce(paste2)#> [1] "a.b.c.d"#> [1] "a-b.c-d"#> [1] "0 2 6 4 8" "1 3 7 5 9"# You can shortcircuit a reduction and terminate it early by # returning a value wrapped in a done(). In the following example # we return early if the result-so-far, which is passed on the LHS, # meets a condition: paste3 <- function(out, input, sep = ".") { if (nchar(out) > 4) { return(done(out)) } paste(out, input, sep = sep) } letters %>% reduce(paste3)#> [1] "a.b.c"# Here the early return branch checks the incoming inputs passed on # the RHS: paste4 <- function(out, input, sep = ".") { if (input == "j") { return(done(out)) } paste(out, input, sep = sep) } letters %>% reduce(paste4)#> [1] "a.b.c.d.e.f.g.h.i"