Skip to content

Commit

Permalink
Add .simplify argument to accumulate()
Browse files Browse the repository at this point in the history
To disable the default simplification
  • Loading branch information
lionel- committed Aug 5, 2020
1 parent 5bff6cd commit 5601db2
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 13 deletions.
10 changes: 10 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@

# purrr (development version)

* Because of historical reasons, `accumulate()` automatically
simplifies the accumulated list to an atomic vector if possible. To
control this behaviour, `accumulate()` gains a `.simplify` argument
which is `TRUE` by default for backward compatibility.

Ideally the simplification step would be performed with `simplify()`
by composition. Since that ship has sailed, we've at least made it
possible to disable the simplification by setting `.simplify` to
`FALSE`.

* `accumulate()` now uses vctrs for simplifying the output. This
ensures a more principled and flexible coercion behaviour.

Expand Down
37 changes: 26 additions & 11 deletions R/reduce.R
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,6 @@ seq_len2 <- function(start, end) {
#' needs to be 1 element shorter than the vector to be accumulated (`.x`).
#' If `.init` is set, `.y` needs to be one element shorted than the
#' concatenation of the initial value and `.x`.
#'
#' @param .f For `accumulate()` `.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.
Expand All @@ -342,17 +341,20 @@ seq_len2 <- function(start, end) {
#'
#' The accumulation terminates early if `.f` returns a value wrapped in
#' a [done()].
#'
#' @param .init If supplied, will be used as the first value to start
#' the accumulation, rather than using `.x[[1]]`. This is useful if
#' you want to ensure that `reduce` returns a correct value when `.x`
#' is empty. If missing, and `.x` is empty, will throw an error.
#'
#' @param .dir The direction of accumulation as a string, one of
#' `"forward"` (the default) or `"backward"`. See the section about
#' direction below.
#' @param .simplify If `TRUE`, the default, the accumulated list of
#' results is simplified to an atomic vector if possible. See
#' [simplify()].
#'
#' @return A vector the same length of `.x` with the same names as `.x`.
#' @return A list the same length of `.x` with the same names as `.x`.
#' If `.simplify` is `TRUE`, the list is simplified to an atomic
#' vector if possible.
#'
#' If `.init` is supplied, the length is extended by 1. If `.x` has
#' names, the initial value is given the name `".init"`, otherwise
Expand Down Expand Up @@ -454,20 +456,22 @@ seq_len2 <- function(start, end) {
#' ggtitle("Simulations of a random walk with drift")
#' }
#' @export
accumulate <- function(.x, .f, ..., .init, .dir = c("forward", "backward")) {
accumulate <- function(.x,
.f,
...,
.init,
.dir = c("forward", "backward"),
.simplify = TRUE) {
.dir <- arg_match(.dir, c("forward", "backward"))
.f <- as_mapper(.f, ...)

res <- reduce_impl(.x, .f, ..., .init = .init, .dir = .dir, .acc = TRUE)
names(res) <- accumulate_names(names(.x), .init, .dir)

# It would be unappropriate to simplify the result rowwise with
# `accumulate()` because it has invariants defined in terms of
# `length()` rather than `vec_size()`
if (some(res, is.data.frame)) {
res
if (.simplify) {
acc_simplify(res)
} else {
vec_simplify(res)
res
}
}
#' @rdname accumulate
Expand All @@ -491,6 +495,17 @@ accumulate_names <- function(nms, init, dir) {
nms
}

acc_simplify <- function(x) {
# It would be unappropriate to simplify the result rowwise with
# `accumulate()` because it has invariants defined in terms of
# `length()` rather than `vec_size()`
if (some(x, is.data.frame)) {
x
} else {
vec_simplify(x)
}
}

#' Reduce from the right (retired)
#'
#' @description
Expand Down
17 changes: 15 additions & 2 deletions man/accumulate.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions tests/testthat/test-reduce.R
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ test_that("accumulate() does not simplify data frame rowwise", {
expect_identical(out, exp)
})

test_that("accumulate() simplifies optionally", {
expect_identical(accumulate(1:3, function(x, y) y), 1:3)
expect_identical(accumulate(1:3, function(x, y) y, .simplify = FALSE), as.list(1:3))
})


# reduce2 -----------------------------------------------------------------

Expand Down

0 comments on commit 5601db2

Please sign in to comment.