-
Notifications
You must be signed in to change notification settings - Fork 292
Expand file tree
/
Copy pathaccumulate.Rd
More file actions
199 lines (166 loc) · 7.36 KB
/
accumulate.Rd
File metadata and controls
199 lines (166 loc) · 7.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/reduce.R
\name{accumulate}
\alias{accumulate}
\alias{accumulate2}
\title{Accumulate intermediate results of a vector reduction}
\usage{
accumulate(
.x,
.f,
...,
.init,
.dir = c("forward", "backward"),
.simplify = NA,
.ptype = NULL
)
accumulate2(.x, .y, .f, ..., .init, .simplify = NA, .ptype = NULL)
}
\arguments{
\item{.x}{A list or atomic vector.}
\item{.f}{For \code{accumulate()} \code{.f} is 2-argument function. The function will
be passed the accumulated result or initial value as the first argument.
The next value in sequence is passed as the second argument.
For \code{accumulate2()}, a 3-argument function. The
function will be passed the accumulated result as the first
argument. The next value in sequence from \code{.x} is passed as the second argument. The
next value in sequence from \code{.y} is passed as the third argument.
The accumulation terminates early if \code{.f} returns a value wrapped in
a \code{\link[=done]{done()}}.}
\item{...}{Additional arguments passed on to the mapped function.
We now generally recommend against using \code{...} to pass additional
(constant) arguments to \code{.f}. Instead use a shorthand anonymous function:
\if{html}{\out{<div class="sourceCode R">}}\preformatted{# Instead of
x |> map(f, 1, 2, collapse = ",")
# do:
x |> map(\\(x) f(x, 1, 2, collapse = ","))
}\if{html}{\out{</div>}}
This makes it easier to understand which arguments belong to which
function and will tend to yield better error messages.}
\item{.init}{If supplied, will be used as the first value to start
the accumulation, rather than using \code{.x[[1]]}. This is useful if
you want to ensure that \code{reduce} returns a correct value when \code{.x}
is empty. If missing, and \code{.x} is empty, will throw an error.}
\item{.dir}{The direction of accumulation as a string, one of
\code{"forward"} (the default) or \code{"backward"}. See the section about
direction below.}
\item{.simplify}{If \code{NA}, the default, the accumulated list of
results is simplified to an atomic vector if possible.
If \code{TRUE}, the result is simplified, erroring if not possible.
If \code{FALSE}, the result is not simplified, always returning a list.}
\item{.ptype}{If \code{simplify} is \code{NA} or \code{TRUE}, optionally supply a vector
prototype to enforce the output type.}
\item{.y}{For \code{accumulate2()} \code{.y} is the second argument of the pair. It
needs to be 1 element shorter than the vector to be accumulated (\code{.x}).
If \code{.init} is set, \code{.y} needs to be one element shorted than the
concatenation of the initial value and \code{.x}.}
}
\value{
A vector the same length of \code{.x} with the same names as \code{.x}.
If \code{.init} is supplied, the length is extended by 1. If \code{.x} has
names, the initial value is given the name \code{".init"}, otherwise
the returned vector is kept unnamed.
If \code{.dir} is \code{"forward"} (the default), the first element is the
initial value (\code{.init} if supplied, or the first element of \code{.x})
and the last element is the final reduced value. In case of a
right accumulation, this order is reversed.
The accumulation terminates early if \code{.f} returns a value wrapped
in a \code{\link[=done]{done()}}. If the done box is empty, the last value is
used instead and the result is one element shorter (but always
includes the initial value, even when terminating at the first
iteration).
}
\description{
\code{accumulate()} sequentially applies a 2-argument function to elements of a
vector. Each application of the function uses the initial value or result
of the previous application as the first argument. The second argument is
the next value of the vector. The results of each application are
returned in a list. The accumulation can optionally terminate before
processing the whole vector in response to a \code{done()} signal returned by
the accumulation function.
By contrast to \code{accumulate()}, \code{reduce()} applies a 2-argument function in
the same way, but discards all results except that of the final function
application.
\code{accumulate2()} sequentially applies a function to elements of two lists, \code{.x} and \code{.y}.
}
\section{Direction}{
When \code{.f} is an associative operation like \code{+} or \code{c()}, the
direction of reduction does not matter. For instance, reducing the
vector \code{1:3} with the binary function \code{+} computes the sum \code{((1 + 2) + 3)} from the left, and the same sum \code{(1 + (2 + 3))} from the
right.
In other cases, the direction has important consequences on the
reduced value. For instance, reducing a vector with \code{list()} from
the left produces a left-leaning nested list (or tree), while
reducing \code{list()} from the right produces a right-leaning list.
}
\examples{
# With an associative operation, the final value is always the
# same, no matter the direction. You'll find it in the first element for a
# backward (left) accumulation, and in the last element for forward
# (right) one:
1:5 |> accumulate(`+`)
1:5 |> accumulate(`+`, .dir = "backward")
# The final value is always equal to the equivalent reduction:
1:5 |> reduce(`+`)
# It is easier to understand the details of the reduction with
# `paste()`.
accumulate(letters[1:5], paste, sep = ".")
# Note how the intermediary reduced values are passed to the left
# with a left reduction, and to the right otherwise:
accumulate(letters[1:5], paste, sep = ".", .dir = "backward")
# By ignoring the input vector (nxt), you can turn output of one step into
# the input for the next. This code takes 10 steps of a random walk:
accumulate(1:10, \(acc, nxt) acc + rnorm(1), .init = 0)
# `accumulate2()` is a version of `accumulate()` that works with
# 3-argument functions and one additional vector:
paste2 <- function(acc, nxt, sep = ".") paste(acc, nxt, sep = sep)
letters[1:4] |> accumulate(paste2)
letters[1:4] |> accumulate2(c("-", ".", "-"), paste2)
# You can shortcircuit an accumulation 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 |> accumulate(paste3)
# Note how we get twice the same value in the accumulation. That's
# because we have returned it twice. To prevent this, return an empty
# done box to signal to accumulate() that it should terminate with the
# value of the last iteration:
paste3 <- function(out, input, sep = ".") {
if (nchar(out) > 4) {
return(done())
}
paste(out, input, sep = sep)
}
letters |> accumulate(paste3)
# Here the early return branch checks the incoming inputs passed on
# the RHS:
paste4 <- function(out, input, sep = ".") {
if (input == "f") {
return(done())
}
paste(out, input, sep = sep)
}
letters |> accumulate(paste4)
# Simulating stochastic processes with drift
\dontrun{
library(dplyr)
library(ggplot2)
map(1:5, \(i) rnorm(100)) |>
set_names(paste0("sim", 1:5)) |>
map(\(l) accumulate(l, \(acc, nxt) .05 + acc + nxt)) |>
map(\(x) tibble(value = x, step = 1:100)) |>
list_rbind(names_to = "simulation") |>
ggplot(aes(x = step, y = value)) +
geom_line(aes(color = simulation)) +
ggtitle("Simulations of a random walk with drift")
}
}
\seealso{
\code{\link[=reduce]{reduce()}} when you only need the final reduced value.
}