-
Notifications
You must be signed in to change notification settings - Fork 292
Description
There's a functional pattern that I think would be useful in purrr: repeat a function until some predicate is met, where the output would then be of an unpredictable size. This would be very useful in iterative algorithms like this EM that run until convergence.
For example, consider this implementation of accumulate_while, which (like accumulate) iteratively changes a value while keeping intermediate states, then stops when a predicate is not met:
accumulate_while <- function(.x, .f, .p, ..., .compare = FALSE, .max = 100000) {
.f <- as_function(.f, ...)
.p <- as_function(.p)
ret <- vector("list", .max)
ret[[1]] <- .x
so_far <- .x
for (i in seq(2, .max)) {
result <- .f(so_far)
ret[[i]] <- result
if (.compare && !.p(so_far, result)) {
break
}
if (!.compare && !.p(result)) {
break
}
so_far <- result
}
if (i == .max) {
message("Reached .max limit of ", .max)
}
head(ret, i)
}
As one minimal example:
# add one until not less than 10
as_vector(accumulate_while(1, ~ . + 1, ~ . < 10))
# [1] 1 2 3 4 5 6 7 8 9 10
There's also a .compare option where the predicate takes the last two values, which is very useful for "run until convergence"
as_vector(accumulate_while(1, ~ . / 2, ~ abs(.x - .y) > 1e-4, .compare = TRUE))
[1] 1.000000e+00 5.000000e-01 2.500000e-01 1.250000e-01 6.250000e-02 3.125000e-02 1.562500e-02
[8] 7.812500e-03 3.906250e-03 1.953125e-03 9.765625e-04 4.882812e-04 2.441406e-04 1.220703e-04
[15] 6.103516e-05I can think of other variations as well:
reduce_while: same, but drop the intermediate statesrerun_while: performs each trial independently until the result satisfies a condition (like a geometric process), keeps the intermediate result. (Technically a special case ofaccumulate_while).
If you support this I can turn it into a pull request with docs and tests.
(Also note that the final version could easily support an option of .max being infinite, and successively doubling the size of ret when it is reached- I left it out for simplicity here).