purrr/0000755000176200001440000000000013171733547011437 5ustar liggesuserspurrr/inst/0000755000176200001440000000000013171650761012410 5ustar liggesuserspurrr/inst/doc/0000755000176200001440000000000013171650761013155 5ustar liggesuserspurrr/inst/doc/other-langs.html0000644000176200001440000002003413171650760016264 0ustar liggesusers Functional programming in other languages

Functional programming in other languages

purrr draws inspiration from many related tools:

However, the goal of purrr is not to try and simulate a purer functional programming language in R; we don’t want to implement a second-class version of Haskell in R. The goal is to give you similar expressiveness to an FP language, while allowing you to write code that looks and works like R:

purrr/inst/doc/other-langs.Rmd0000644000176200001440000000406013124217327016037 0ustar liggesusers--- title: "Functional programming in other languages" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Functional programming in other languages} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- purrr draws inspiration from many related tools: * List operations defined in the Haskell [prelude][haskell] * Scala's [list methods][scala]. * Functional programming librarys for javascript: [underscore.js](http://underscorejs.org), [lodash](https://lodash.com) and [lazy.js](http://danieltao.com/lazy.js/). * [rlist](http://renkun.me/rlist/), another R package to support working with lists. Similar goals but somewhat different philosophy. However, the goal of purrr is not to try and simulate a purer functional programming language in R; we don't want to implement a second-class version of Haskell in R. The goal is to give you similar expressiveness to an FP language, while allowing you to write code that looks and works like R: * Instead of point free (tacit) style, we use the pipe, `%>%`, to write code that can be read from left to right. * Instead of currying, we use `...` to pass in extra arguments. * Anonymous functions are verbose in R, so we provide two convenient shorthands. For unary functions, `~ .x + 1` is equivalent to `function(.x) .x + 1`. For chains of transformations functions, `. %>% f() %>% g()` is equivalent to `function(.) . %>% f() %>% g()` (this shortcut is provided by magrittr). * R is weakly typed, so we need `map` variants that describe the output type (like `map_int()`, `map_dbl()`, etc) because don't the return type of `.f`. * R has named arguments, so instead of providing different functions for minor variations (e.g. `detect()` and `detectLast()`) we use a named argument, `.right`. Type-stable functions are easy to reason about so additional arguments will never change the type of the output. [scala]:http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.List [haskell]:http://hackage.haskell.org/package/base-4.7.0.1/docs/Prelude.html#g:11 purrr/tests/0000755000176200001440000000000013171650762012576 5ustar liggesuserspurrr/tests/testthat.R0000644000176200001440000000006612465413273014562 0ustar liggesuserslibrary(testthat) library(purrr) test_check("purrr") purrr/tests/testthat/0000755000176200001440000000000013171733547014441 5ustar liggesuserspurrr/tests/testthat/test-utils.R0000644000176200001440000000145613124217327016676 0ustar liggesuserscontext("utils") test_that("%@% is an infix attribute accessor", { expect_identical(mtcars %@% "names", attr(mtcars, "names")) }) test_that("rbernoulli is a special case of rbinom", { set.seed(1) x <- rbernoulli(10) set.seed(1) y <- ifelse(rbinom(10, 1, 0.5) == 1, TRUE, FALSE) expect_equal(x, y) }) test_that("rdunif works", { expect_length(rdunif(100, 10), 100) }) test_that("rdunif fails if a and b are not unit length numbers", { expect_error(rdunif(1000, 1, "a")) expect_error(rdunif(1000, 1, c(0.5, 0.2))) expect_error(rdunif(1000, FALSE, 2)) expect_error(rdunif(1000, c(2, 3), 2)) }) test_that("has_names returns vector of logicals", { expect_equal(has_names(letters %>% set_names()), rep_along(letters, TRUE)) expect_equal(has_names(letters), rep_along(letters, FALSE)) }) purrr/tests/testthat/test-pluck.R0000644000176200001440000000776413137651733016673 0ustar liggesuserscontext("pluck") test_that("contents must be a vector", { expect_error(pluck(quote(x), list(1)), "Don't know how to pluck") }) # pluck vector -------------------------------------------------------------- test_that("can pluck by position", { x <- list("a", 1, c(TRUE, FALSE)) # double expect_identical(pluck(x, list(1)), x[[1]]) expect_identical(pluck(x, list(2)), x[[2]]) expect_identical(pluck(x, list(3)), x[[3]]) # integer expect_identical(pluck(x, list(1L)), x[[1]]) expect_identical(pluck(x, list(2L)), x[[2]]) expect_identical(pluck(x, list(3L)), x[[3]]) }) test_that("can pluck by name", { x <- list(a = "a", b = 1, c = c(TRUE, FALSE)) expect_identical(pluck(x, list("a")), x[["a"]]) expect_identical(pluck(x, list("b")), x[["b"]]) expect_identical(pluck(x, list("c")), x[["c"]]) }) test_that("can pluck from atomic vectors", { expect_identical(pluck(TRUE, list(1)), TRUE) expect_identical(pluck(1L, list(1)), 1L) expect_identical(pluck(1, list(1)), 1) expect_identical(pluck("a", list(1)), "a") }) test_that("can pluck by name and position", { x <- list(a = list(list(b = 1))) expect_equal(pluck(x, list("a", 1, "b")), 1) }) test_that("require length 1 vectors", { expect_error(pluck(1, list(letters)), "must have length 1") expect_error(pluck(1, list(TRUE)), "must be a character or numeric") }) test_that("special indexes never match", { x <- list(a = 1, b = 2, c = 3) expect_null(pluck(x, list(NA_character_))) expect_null(pluck(x, list(""))) expect_null(pluck(x, list(NA_integer_))) expect_null(pluck(x, list(NA_real_))) expect_null(pluck(x, list(NaN))) expect_null(pluck(x, list(Inf))) expect_null(pluck(x, list(-Inf))) }) test_that("special values return NULL", { # unnamed input expect_null(pluck(list(1, 2), list("a"))) # zero length input expect_null(pluck(integer(), list(1))) # past end expect_null(pluck(1:4, list(10))) expect_null(pluck(1:4, list(10L))) }) test_that("handles weird names", { x <- list(1, 2, 3, 4, 5) names(x) <- c("a", "a", NA, "", "b") expect_equal(pluck(x, list("a")), 1) expect_equal(pluck(x, list("b")), 5) expect_null(pluck(x, list(""))) expect_null(pluck(x, list(NA_character_))) }) # closures ---------------------------------------------------------------- test_that("can pluck attributes", { x <- structure( list( structure( list(), x = 1 ) ), y = 2 ) expect_equal(pluck(x, list(attr_getter("y"))), 2) expect_equal(pluck(x, list(1, attr_getter("x"))), 1) }) test_that("attr_getter() evaluates eagerly", { getters <- list_len(2) attrs <- c("foo", "bar") for (i in seq_along(attrs)) { getters[[i]] <- attr_getter(attrs[[i]]) } x <- set_attrs(list(), foo = "foo", bar = "bar") expect_identical(getters[[1]](x), "foo") }) test_that("delegate error handling to Rf_eval()", { expect_error(pluck(letters, list(function() NULL)), "unused argument") expect_error(pluck(letters, list(function(x, y) y)), "missing, with no default") }) # environments ------------------------------------------------------------ test_that("pluck errors with invalid indices", { expect_error(pluck(environment(), list(1)), "not a string") expect_error(pluck(environment(), list(letters)), "not a string") }) test_that("pluck returns missing with missing index", { expect_equal(pluck(environment(), list(NA_character_)), NULL) }) test_that("plucks by name", { env <- new.env(parent = emptyenv()) env$x <- 10 expect_equal(pluck(env, list("x")), 10) }) # S4 ---------------------------------------------------------------------- newA <- methods::setClass("A", list(a = "numeric", b = "numeric")) A <- newA(a = 1, b = 10) test_that("pluck errors with invalid indices", { expect_error(pluck(A, list(1)), "not a string") expect_error(pluck(A, list(letters)), "not a string") }) test_that("pluck returns missing with missing index", { expect_equal(pluck(A, list(NA_character_)), NULL) }) test_that("plucks by name", { expect_equal(pluck(A, list("a")), 1) }) purrr/tests/testthat/test-splice.R0000644000176200001440000000101113124217327017000 0ustar liggesuserscontext("splice") test_that("predicate controls which elements get spliced", { x <- list(1, 2, list(3, 4)) expect_equal(splice_if(x, ~ FALSE), x) expect_equal(splice_if(x, is.list), list(1, 2, 3, 4)) }) test_that("splice() produces correctly named lists", { inputs <- list(arg1 = "a", arg2 = "b") out1 <- splice(inputs, arg3 = c("c1", "c2")) expect_named(out1, c("arg1", "arg2", "arg3")) out2 <- splice(inputs, arg = list(arg3 = 1, arg4 = 2)) expect_named(out2, c("arg1", "arg2", "arg3", "arg4")) }) purrr/tests/testthat/test-output.R0000644000176200001440000000177313137651733017107 0ustar liggesuserscontext("output") test_that("safely has NULL error when successful", { out <- safely(log10)(10) expect_equal(out, list(result = 1, error = NULL)) }) test_that("safely has NULL result on failure", { out <- safely(log10)("a") expect_equal(out$result, NULL) expect_equal(out$error$message, "non-numeric argument to mathematical function") }) test_that("quietly captures output", { f <- function() { cat(1) message(2, appendLF = FALSE) warning(3) 4 } expect_output(quietly(f)(), NA) expect_message(quietly(f)(), NA) expect_warning(quietly(f)(), NA) out <- quietly(f)() expect_equal(out, list( result = 4, output = "1", warnings = "3", messages = "2" )) }) test_that("possibly returns default value on failure", { expect_identical(possibly(log, NA_real_)("a"), NA_real_) }) test_that("auto_browse() not intended for primitive functions", { expect_error(auto_browse(log)(NULL), "primitive functions") expect_error(auto_browse(identity)(NULL), NA) }) purrr/tests/testthat/test-as-mapper.R0000644000176200001440000000423313137651733017426 0ustar liggesuserscontext("as_mapper") # formulas ---------------------------------------------------------------- test_that("can refer to first argument in three ways", { expect_equal(map_dbl(1, ~ . + 1), 2) expect_equal(map_dbl(1, ~ .x + 1), 2) expect_equal(map_dbl(1, ~ ..1 + 1), 2) }) test_that("can refer to second arg in two ways", { expect_equal(map2_dbl(1, 2, ~ .x + .y + 1), 4) expect_equal(map2_dbl(1, 2, ~ ..1 + ..2 + 1), 4) }) # vectors -------------------------------------------------------------- # test_that(".null generates warning", { # expect_warning(map(1, 2, .null = NA), "`.null` is deprecated") # }) test_that(".default replaces absent values", { x <- list( list(a = 1, b = 2, c = 3), list(a = 1, c = 2), NULL ) expect_equal(map_dbl(x, 3, .default = NA), c(3, NA, NA)) expect_equal(map_dbl(x, "b", .default = NA), c(2, NA, NA)) }) test_that(".default replaces elements with length 0", { x <- list( list(a = 1), list(a = NULL), list(a = numeric()) ) expect_equal(map_dbl(x, "a", .default = NA), c(1, NA, NA)) }) test_that("Additional arguments are ignored", { expect_equal(as_mapper(function() NULL, foo = "bar", foobar), function() NULL) }) test_that("can supply length > 1 vectors", { expect_identical(as_mapper(1:2)(list(list("a", "b"))), "b") expect_identical(as_mapper(c("a", "b"))(list(a = list("a", b = "b"))), "b") }) # primitive functions -------------------------------------------------- test_that("primitive functions are wrapped", { expect_identical(as_mapper(`-`)(.y = 10, .x = 5), -5) expect_identical(as_mapper(`c`)(1, 3, 5), c(1, 3, 5)) }) test_that("syntactic primitives are wrapped", { expect_identical(as_mapper(`[[`)(mtcars, "cyl"), mtcars$cyl) expect_identical(as_mapper(`$`)(mtcars, cyl), mtcars$cyl) }) # lists ------------------------------------------------------------------ test_that("lists are wrapped", { mapper_list <- as_mapper(list("mpg", 5))(mtcars) base_list <- mtcars[["mpg"]][[5]] expect_identical(mapper_list, base_list) }) test_that("raw and complex types aren't supported for indexing", { expect_error(as_mapper(1)(raw(2))) expect_error(as_mapper(1)(complex(2))) }) purrr/tests/testthat/test-head-tail.R0000644000176200001440000000064013124217327017360 0ustar liggesuserscontext("head-tail") y <- 1:100 test_that("head_while works", { expect_length(head_while(y, function(x) x <= 15), 15) }) test_that("tail_while works", { expect_length(tail_while(y, function(x) x >= 86), 15) }) test_that("original vector returned if predicate satisfied by all elements", { expect_identical(head_while(y, function(x) x <= 100), y) expect_identical(tail_while(y, function(x) x >= 0), y) }) purrr/tests/testthat/test-list-modify-update.R0000644000176200001440000000432213137651733021260 0ustar liggesuserscontext("list_modify") # list_modify ------------------------------------------------------------- test_that("named lists have values replaced by name", { expect_equal(list_modify(list(a = 1), b = 2), list(a = 1, b = 2)) expect_equal(list_modify(list(a = 1), a = 2), list(a = 2)) expect_equal(list_modify(list(a = 1, b = 2), b = NULL), list(a = 1)) }) test_that("unnamed lists are replaced by position", { expect_equal(list_modify(list(3), 1, 2), list(1, 2)) expect_equal(list_modify(list(1, 2, 3), 4), list(4, 2, 3)) expect_equal(list_modify(list(1, 2, 3), NULL, NULL), list(3)) }) test_that("error if one named and the other is not", { expect_error( list_modify(list(a = 1), 2), "must be either both named or both unnamed" ) }) test_that("lists are replaced recursively", { expect_equal( list_modify( list(a = list(x = 1)), a = list(x = 2) ), list(a = list(x = 2)) ) expect_equal( list_modify( list(a = list(x = 1)), a = list(y = 2) ), list(a = list(x = 1, y = 2)) ) }) # list_merge -------------------------------------------------------------- test_that("list_merge concatenates values from two lists", { l1 <- list(x = 1:10, y = 4, z = list(a = 1, b = 2)) l2 <- list(x = 11, z = list(a = 2:5, c = 3)) l <- list_merge(l1, !!! l2) expect_equal(l$x, c(l1$x, l2$x)) expect_equal(l$y, c(l1$y, l2$y)) expect_equal(l$z$a, c(l1$z$a, l2$z$a)) expect_equal(l$z$b, c(l1$z$b, l2$z$b)) expect_equal(l$z$c, c(l1$z$c, l2$z$c)) }) test_that("list_merge concatenates without needing names", { l1 <- list(1:10, 4, list(1, 2)) l2 <- list(11, 5, list(2:5, 3)) expect_length(list_merge(l1, !!! l2), 3) }) test_that("list_merge returns the non-empty list", { expect_equal(list_merge(list(3)), list(3)) expect_equal(list_merge(list(), 2), set_names(list(2), "")) }) # update_list ------------------------------------------------------------ test_that("can modify element called x", { expect_equal(update_list(list(), x = 1), list(x = 1)) }) test_that("quosures and formulas are evaluated", { expect_identical(update_list(list(x = 1), y = quo(x + 1)), list(x = 1, y = 2)) expect_identical(update_list(list(x = 1), y = ~x + 1), list(x = 1, y = 2)) }) purrr/tests/testthat/test-along.R0000644000176200001440000000036713124217327016636 0ustar liggesuserscontext("along") test_that("list_along works", { x <- 1:5 expect_identical(list_along(x), vector("list", 5)) }) test_that("rep_along works", { expect_equal( rep_along(c("c", "b", "a"), 1:3), rep_along(c("d", "f", "e"), 1:3) ) }) purrr/tests/testthat/test-predicates.R0000644000176200001440000000071513124217327017656 0ustar liggesuserscontext("predicates") test_that("predicate-based functionals work with logical vectors", { expect_equal(keep(as.list(1:3), c(TRUE, FALSE, TRUE)), list(1, 3)) expect_equal(discard(as.list(1:3), c(TRUE, FALSE, TRUE)), list(2)) expect_equal( modify_if(as.list(1:3), c(TRUE, FALSE, TRUE), as.character), list("1", 2, "3") ) expect_equal( lmap_if(as.list(1:3), c(TRUE, FALSE, TRUE), ~list(as.character(.x[[1]]))), list("1", 2, "3") ) }) purrr/tests/testthat/test-cross.R0000644000176200001440000000130113124217327016654 0ustar liggesuserscontext("cross") test_that("long format corresponds to expand.grid output", { x <- list(a = 1:3, b = 4:9) out1 <- cross_df(x) out2 <- expand.grid(x, KEEP.OUT.ATTRS = FALSE) %>% dplyr::as_data_frame() expect_equal(out1, out2) }) test_that("filtering works", { filter <- function(x, y) x >= y out <- cross2(1:3, 1:3, .filter = filter) expect_equal(out, list(list(1, 2), list(1, 3), list(2, 3))) }) test_that("filtering fails when filter function doesn't return a logical", { filter <- function(x, y, z) x + y + z expect_error(cross3(1:3, 1:3, 1:3, .filter = filter)) }) test_that("works with empty input", { expect_equal(cross(list()), list()) expect_equal(cross(NULL), NULL) }) purrr/tests/testthat/test-composition.R0000644000176200001440000000146713124217327020103 0ustar liggesuserscontext("composition") test_that("lift_dl and lift_ld are inverses of each other", { expect_identical( sum %>% lift_dl(.unnamed = TRUE) %>% invoke(list(3, NA, 4, na.rm = TRUE)), sum %>% lift_dl() %>% lift_ld() %>% invoke(3, NA, 4, na.rm = TRUE) ) }) test_that("lift_dv is from ... to c(...)", { expect_equal(lift_dv(range, .unnamed = TRUE)(1:10), c(1, 10)) }) test_that("lift_vd is from c(...) to ...", { expect_equal(lift_vd(mean)(1, 2), 1.5) }) test_that("lift_vl is from c(...) to list(...)", { expect_equal(lift_vl(mean)(list(1, 2)), 1.5) }) test_that("lift_lv is from list(...) to c(...)", { glue <- function(l) { if (!is.list(l)) stop("not a list") l %>% invoke(paste, .) } expect_identical(lift_lv(glue)(letters), paste(letters, collapse = " ")) }) purrr/tests/testthat/test-compose.R0000644000176200001440000000042013124217327017171 0ustar liggesuserscontext("compose") test_that("composed functions are applied right to left", { expect_identical(!is.null(4), compose(`!`, is.null)(4)) set.seed(1) x <- sample(1:4, 100, replace = TRUE) expect_identical(unname(sort(table(x))), compose(unname, sort, table)(x)) }) purrr/tests/testthat/test-partial.R0000644000176200001440000000205713124217327017170 0ustar liggesuserscontext("partial") test_that("dots are correctly placed in the signature", { dots_last_actual <- call("runif", n = call("rpois", 1, 5), quote(...)) dots_last_alleged <- partial(runif, n = rpois(1, 5)) %>% body() expect_identical(dots_last_actual, dots_last_alleged) # Also tests that argument names are not eaten when .dots_first = TRUE dots_first_actual <- call("runif", quote(...), n = call("rpois", 1, 5)) dots_first_alleged <- partial(runif, n = rpois(1, 5), .first = FALSE) %>% body() expect_identical(dots_first_actual, dots_first_alleged) }) test_that("partial() works with no partialised arguments", { actual <- call("runif", quote(...)) alleged1 <- partial(runif, .first = TRUE) %>% body() alleged2 <- partial(runif, .first = FALSE) %>% body() expect_identical(actual, alleged1) expect_identical(actual, alleged2) }) test_that("lazy evaluation means arguments aren't repeatedly evaluated", { f <- partial(runif, n = rpois(1, 5), .lazy = FALSE) .n <- 100 v <- map_int(rerun(.n, f()), length) expect_true(table(v) == .n) }) purrr/tests/testthat/test-map_n.R0000644000176200001440000000365713124217327016635 0ustar liggesuserscontext("pmap") test_that("input must be a list of vectors", { expect_error(pmap(environment(), identity), "not a list") expect_error(pmap(list(environment()), identity), "not a vector") }) test_that("elements must be same length", { expect_error(pmap(list(1:2, 1:3), identity), "has length 2") }) test_that("handles any length 0 input", { expect_equal(pmap(list(list(), list(), list()), ~ 1), list()) expect_equal(pmap(list(NULL, NULL, NULL), ~ 1), list()) expect_equal(pmap(list(list(), list(), 1:10), ~ 1), list()) expect_equal(pmap(list(NULL, NULL, 1:10), ~ 1), list()) }) test_that("length 1 elemetns are recycled", { out <- pmap(list(1:2, 1), c) expect_equal(out, list(c(1, 1), c(2, 1))) }) test_that(".f called with named arguments", { out <- pmap(list(x = 1, 2, y = 3), list)[[1]] expect_equal(names(out), c("x", "", "y")) }) test_that("names are preserved", { out <- pmap(list(c(x = 1, y = 2), 3:4), list) expect_equal(names(out), c("x", "y")) }) test_that("... are passed on", { out <- pmap(list(x = 1:2), list, n = 1) expect_equal(out, list( list(x = 1, n = 1), list(x = 2, n = 1) )) }) test_that("outputs are suffixes have correct type", { x <- 1:3 expect_is(pmap_lgl(list(x), is.numeric), "logical") expect_is(pmap_int(list(x), length), "integer") expect_is(pmap_dbl(list(x), mean), "numeric") expect_is(pmap_chr(list(x), paste), "character") expect_is(pmap_dfr(list(x), as.data.frame), "data.frame") expect_is(pmap_dfc(list(x), as.data.frame), "data.frame") }) test_that("pmap on data frames performs rowwise operations", { mtcars2 <- mtcars[c("mpg", "cyl")] expect_length(pmap(mtcars2, paste), nrow(mtcars)) expect_is(pmap_lgl(mtcars2, function(mpg, cyl) mpg > cyl), "logical") expect_is(pmap_int(mtcars2, function(mpg, cyl) as.integer(cyl)), "integer") expect_is(pmap_dbl(mtcars2, function(mpg, cyl) mpg + cyl), "numeric") expect_is(pmap_chr(mtcars2, paste), "character") }) purrr/tests/testthat/test-invoke.R0000644000176200001440000000300013124217327017014 0ustar liggesuserscontext("invoke") # invoke ------------------------------------------------------------------ test_that("invoke() evaluates expressions in the right environment", { x <- letters f <- toupper expect_equal(invoke("f", quote(x)), toupper(letters)) }) test_that("invoke() follows promises to find the evaluation env", { x <- letters f <- toupper f1 <- function(y) { f2 <- function(z) purrr::invoke(z, quote(x)) f2(y) } expect_equal(f1("f"), toupper(letters)) }) # invoke_map -------------------------------------------------------------- test_that("invoke_map() works with bare function", { data <- list(1:2, 3:4) expected <- list("1 2", "3 4") expect_equal(invoke_map(paste, data), expected) expect_equal(invoke_map("paste", data), expected) expect_equal(invoke_map_chr(paste, data), unlist(expected)) expect_identical(invoke_map_dbl(`+`, data), c(3, 7)) expect_identical(invoke_map_int(`+`, data), c(3L, 7L)) expect_identical(invoke_map_lgl(`&&`, data), c(TRUE, TRUE)) ops <- set_names(c(`+`, `-`), c("a", "b")) expect_identical(invoke_map_dfr(ops, data), invoke_map_dfc(ops, data)) }) test_that("invoke_map() evaluates expressions in the right environment", { shadowed_object <- letters shadowed_fun <- toupper expect_equal( invoke_map("shadowed_fun", list(quote(shadowed_object))), list(toupper(letters)) ) }) test_that("invoke_maps doesn't rely on c() returning list", { day <- as.Date("2016-09-01") expect_equal(invoke_map(identity, list(day)), list(day)) }) purrr/tests/testthat/test-recycle_args.R0000644000176200001440000000123413124217327020172 0ustar liggesuserscontext("recycle_args") test_that("rejects uneven lengths", { args <- list(1, c(1:2), NULL) expect_error(purrr:::recycle_args(args), "lengths == 1L \\| lengths == n") }) test_that("recycles single values and preserves longer ones", { args <- list(1, 1:12, month.name, "a") recycled <- purrr:::recycle_args(args) expect_equal(recycled[[1]], rep(1, 12)) expect_equal(recycled[[2]], 1:12) expect_equal(recycled[[3]], month.name) expect_equal(recycled[[4]], rep("a", 12)) }) test_that("will not recycle non-vectors", { args <- list(1:12, identity) expect_error( purrr:::recycle_args(args), "replicate an object of type 'closure'" ) }) purrr/tests/testthat/test-reduce.R0000644000176200001440000000345513124217327017006 0ustar liggesuserscontext("reduce") test_that("empty input returns init or error", { expect_error(reduce(list()), "no `.init` supplied") expect_equal(reduce(list(), `+`, .init = 0), 0) }) test_that("first/value value used as first value", { x <- c(1, 1) expect_equal(reduce(x, `+`), 2) expect_equal(reduce(x, `+`, .init = 1), 3) expect_equal(reduce_right(x, `+`), 2) expect_equal(reduce_right(x, `+`, .init = 1), 3) }) test_that("length 1 argument reduced with init", { expect_equal(reduce(1, `+`, .init = 1), 2) expect_equal(reduce_right(1, `+`, .init = 1), 2) }) test_that("reduce_right equivalent to reversing input", { x <- list(c(2, 1), c(4, 3), c(6, 5)) expect_equal(reduce_right(x, c), c(6, 5, 4, 3, 2, 1)) expect_equal(reduce_right(x, c, .init = 7), c(7, 6, 5, 4, 3, 2, 1)) }) # accumulate -------------------------------------------------------------- test_that("accumulate passes arguments to function", { tt <- c("a", "b", "c") expect_equal(accumulate(tt, paste, sep = "."), c("a", "a.b", "a.b.c")) expect_equal(accumulate_right(tt, paste, sep = "."), c("c.b.a", "c.b", "c")) }) # reduce2 ----------------------------------------------------------------- test_that("basic application works", { paste2 <- function(x, y, sep) paste(x, y, sep = sep) x <- c("a", "b", "c") expect_equal(reduce2(x, c("-", "."), paste2), "a-b.c") expect_equal(reduce2(x, c(".", "-", "."), paste2, .init = "x"), "x.a-b.c") }) test_that("reduce2_right works if lengths match", { x <- list(c(0, 1), c(2, 3), c(4, 5)) y <- list(c(6, 7), c(8, 9)) expect_equal(reduce2_right(x, paste, y), c("4 2 8 0 6", "5 3 9 1 7")) expect_error(reduce2_right(y, paste, x)) }) test_that("reduce returns original input if it was length one", { x <- list(c(0, 1), c(2, 3), c(4, 5)) expect_equal(reduce(x[1], paste), x[[1]]) }) purrr/tests/testthat/test-coerce.R0000644000176200001440000000444413124217327016776 0ustar liggesuserscontext("coerce") test_that("missing values converted to new type", { expect_equal(coerce_int(NA), NA_integer_) expect_equal(coerce_dbl(NA), NA_real_) expect_equal(coerce_chr(NA), NA_character_) expect_equal(coerce_dbl(NA_integer_), NA_real_) expect_equal(coerce_chr(NA_integer_), NA_character_) expect_equal(coerce_chr(NA_real_), NA_character_) }) test_that("can't coerce downwards", { expect_error(coerce_chr(list(1)), "Can't coerce") expect_error(coerce_dbl(list(1)), "Can't coerce") expect_error(coerce_int(list(1)), "Can't coerce") expect_error(coerce_lgl(list(1)), "Can't coerce") expect_error(coerce_dbl("a"), "Can't coerce") expect_error(coerce_int("a"), "Can't coerce") expect_error(coerce_lgl("a"), "Can't coerce") expect_error(coerce_int(1), "Can't coerce") expect_error(coerce_lgl(1), "Can't coerce") expect_error(coerce_lgl(1L), "Can't coerce") }) test_that("coercing to same type returns input", { expect_equal(coerce_lgl(c(TRUE, FALSE)), c(TRUE, FALSE)) expect_equal(coerce_dbl(c(1, 2)), c(1, 2)) expect_equal(coerce_int(c(1L, 2L)), c(1L, 2L)) expect_equal(coerce_chr(c("a", "b")), c("a", "b")) }) test_that("types automatically coerced upwards", { expect_identical(coerce_int(c(FALSE, TRUE)), c(0L, 1L)) expect_identical(coerce_dbl(c(FALSE, TRUE)), c(0, 1)) expect_identical(coerce_dbl(c(1L, 2L)), c(1, 2)) expect_identical(coerce_chr(c(FALSE, TRUE)), c("FALSE", "TRUE")) expect_identical(coerce_chr(c(1L, 2L)), c("1", "2")) expect_identical(coerce_chr(c(1.5, 2.5)), c("1.500000", "2.500000")) }) test_that("coercing to character values correctly", { expect_equal(coerce_chr(c(FALSE, TRUE)), c("FALSE", "TRUE")) expect_equal(coerce_chr(c(1L, 2L)), c("1", "2")) expect_equal(coerce_chr(c(1.5, 2.5)), c("1.500000", "2.500000")) expect_equal(coerce_chr(c("a", "b")), c("a", "b")) x <- c(NA, NaN, Inf, -Inf) expect_equal(coerce(x, "character"), as.character(x)) }) test_that("can't coerce to expressions", { expect_error(coerce(list(1), "expression")) }) test_that("as_vector can be type-specifc", { expect_identical(as_vector(as.list(letters), "character"), letters) }) test_that("as_vector cannot coerce lists with zero-length elements", { x <- list(a = 1, b = c(list(), 3)) expect_error(as_vector(x)) expect_identical(x, simplify(x)) }) purrr/tests/testthat/test-arrays.R0000644000176200001440000000076413124217327017040 0ustar liggesuserscontext("arrays") x <- array(1:12, c(2, 2, 3)) test_that("array_branch creates a flat list when no margin specified", { expect_length(array_branch(x), 12) }) test_that("array_branch wraps array in list when margin has length 0", { expect_identical(array_branch(x, numeric(0)), list(x)) }) test_that("length depends on whether list is flattened or not", { m1 <- c(3, 1) m2 <- 3 expect_length(array_branch(x, m1), prod(dim(x)[m1])) expect_length(array_tree(x, m1), prod(dim(x)[m2])) }) purrr/tests/testthat/test-when.R0000644000176200001440000000201313124217327016465 0ustar liggesuserscontext("when") test_that("when chooses the correct action", { x <- 1:5 %>% when( sum(.) <= 50 ~ sum(.), sum(.) <= 100 ~ sum(.) / 2, ~ 0 ) expect_equal(x, 15) y <- 1:10 %>% when( sum(.) <= 50 ~ sum(.), sum(.) <= 100 ~ sum(.) / 2, ~ 0 ) expect_equal(y, sum(1:10) / 2) z <- 1:100 %>% when( sum(.) <= 50 ~ sum(.), sum(.) <= 100 ~ sum(.) / 2, ~ 0 ) expect_equal(z, 0) }) test_that("named arguments work with when", { x <- 1:10 %>% when( sum(.) <= x ~ sum(.) * x, sum(.) <= 2 * x ~ sum(.) * x / 2, ~ 0, x = 60 ) expect_equal(x, sum(1:10) * 60) }) test_that("default values work without a formula", { x <- iris %>% subset(Sepal.Length > 10) %>% when( nrow(.) > 0 ~ ., head(iris, 10) ) expect_equivalent(x, head(iris, 10)) }) test_that("error when named arguments have no matching conditions", { expect_error(1:5 %>% when(a = sum(.) < 5 ~ 3)) }) purrr/tests/testthat/test-imap.R0000644000176200001440000000126113124217327016456 0ustar liggesuserscontext("imap") x <- 1:3 %>% set_names() test_that("imap is special case of map2", { expect_identical(imap(x, paste), map2(x, names(x), paste)) }) test_that("imap always returns a list", { expect_is(imap(x, paste), "list") }) test_that("atomic vector imap works", { expect_true(all(imap_lgl(x, `==`))) expect_length(imap_chr(x, paste), 3) expect_equal(imap_int(x, ~ .x + as.integer(.y)), x * 2) expect_equal(imap_dbl(x, ~ .x + as.numeric(.y)), x * 2) }) test_that("data frame imap works", { expect_identical(imap_dfc(x, paste), imap_dfr(x, paste)) }) test_that("iwalk returns invisibly", { expect_output(iwalk(mtcars, ~ cat(.y, ": ", median(.x), "\n", sep = ""))) }) purrr/tests/testthat/test-find-position.R0000644000176200001440000000172713137651733020330 0ustar liggesuserscontext("find-position") y <- 4:10 test_that("detect functions work", { is_odd <- function(x) x %% 2 == 1 expect_equal(detect(y, is_odd), 5) expect_equal(detect_index(y, is_odd), 2) expect_equal(detect(y, is_odd, .right = TRUE), 9) expect_equal(detect_index(y, is_odd, .right = TRUE), 6) }) test_that("detect returns NULL when match not found", { expect_null(detect(y, function(x) x > 11)) }) test_that("detect_index returns 0 when match not found", { expect_equal(detect_index(y, function(x) x > 11), 0) }) test_that("has_element checks whether a list contains an object", { expect_true(has_element(list(1, 2), 1)) expect_false(has_element(list(1, 2), 3)) }) test_that("detect functions still work with `.p`", { is_even <- function(x) x %% 2 == 0 expect_warning(regex = "renamed to `.f`", expect_identical(detect(1:3, .p = is_even), 2L) ) expect_warning(regex = "renamed to `.f`", expect_identical(detect_index(1:3, .p = is_even), 2L) ) }) purrr/tests/testthat/test-depth.R0000644000176200001440000000125713137651733016650 0ustar liggesuserscontext("depth") test_that("depth of NULL is 0", { expect_equal(vec_depth(NULL), 0L) }) test_that("depth of atomic vector is 1", { expect_equal(vec_depth(1:10), 1) expect_equal(vec_depth(letters), 1) expect_equal(vec_depth(c(TRUE, FALSE)), 1) }) test_that("depth of empty list is 1", { expect_equal(vec_depth(list()), 1) }) test_that("depth of nested is depth of deepest element + 1", { x <- list( NULL, list(), list(list()) ) depths <- map_int(x, vec_depth) expect_equal(depths, c(0, 1, 2)) expect_equal(vec_depth(x), max(depths) + 1) }) test_that("depth throws an error if input is not a vector", { expect_error(vec_depth(as.formula(y ~ x))) }) purrr/tests/testthat/test-prepend.R0000644000176200001440000000040513124217327017164 0ustar liggesuserscontext("prepend") test_that("prepend is clearer version of merging with c()", { x <- 1:3 expect_identical( x %>% prepend(4), x %>% c(4, .) ) expect_identical( x %>% prepend(4, before = 3), x %>% { c(.[1:2], 4, .[3]) } ) }) purrr/tests/testthat/test-lmap.R0000644000176200001440000000035113124217327016460 0ustar liggesuserscontext("lmap") test_that("lmap output is list if input is list", { expect_is(lmap(as.list(mtcars), as.list), "list") }) test_that("lmap output is tibble if input is data frame", { expect_is(lmap(mtcars, as.list), "tbl_df") }) purrr/tests/testthat/test-simplify.R0000644000176200001440000000173512602231320017357 0ustar liggesuserscontext("simplify") test_that("can_simplify() understands vector molds", { x <- as.list(1:3) x2 <- c(x, list(1:3)) expect_true(can_simplify(x, integer(1))) expect_false(can_simplify(x, character(1))) expect_false(can_simplify(x2, integer(1))) x3 <- list(1:2, 3:4, 5:6) expect_true(can_simplify(x3, integer(2))) expect_false(can_simplify(x, integer(2))) }) test_that("can_simplify() understands types as strings", { x <- as.list(1:3) expect_true(can_simplify(x, "integer")) expect_false(can_simplify(x, "character")) }) test_that("integer is coercible to double", { x <- list(1L, 2L) expect_true(can_simplify(x, "numeric")) expect_true(can_simplify(x, numeric(1))) expect_true(can_simplify(x, "double")) expect_true(can_simplify(x, double(1))) }) test_that("numeric is an alias for double", { expect_true(can_simplify(list(1, 2), "numeric")) }) test_that("double is not coercible to integer", { expect_false(can_simplify(list(1, 2), "integer")) }) purrr/tests/testthat/test-rerun.R0000644000176200001440000000073213124217327016665 0ustar liggesuserscontext("rerun") test_that("single unnamed arg doesn't get extra list", { expect_equal(rerun(2, 1), list(1, 1)) }) test_that("single named arg gets extra list", { expect_equal(rerun(2, a = 1), list(list(a = 1), list(a = 1))) }) test_that("every run is different", { x <- rerun(2, runif(1)) expect_true(x[[1]] != x[[2]]) }) test_that("rerun uses scope of expression", { f <- function(n) { rerun(1, x = seq_len(n)) } expect_equal(f(10)[[1]]$x, 1:10) }) purrr/tests/testthat/test-transpose.R0000644000176200001440000000563413102346452017554 0ustar liggesuserscontext("transpose") test_that("input must be a list", { expect_error(transpose(1:3), "is not a list") }) test_that("elements of input must be vectors", { expect_error(transpose(list(environment())), "is not a vector") expect_error(transpose(list(list(), environment())), "is not a vector") }) test_that("empty list returns empty list", { expect_equal(transpose(list()), list()) }) test_that("transpose switches order of first & second idnex", { x <- list(list(1, 3), list(2, 4)) expect_equal(transpose(x), list(list(1, 2), list(3, 4))) }) test_that("inside names become outside names", { x <- list(list(x = 1), list(x = 2)) expect_equal(transpose(x), list(x = list(1, 2))) }) test_that("outside names become inside names", { x <- list(x = list(1, 3), y = list(2, 4)) expect_equal(transpose(x), list(list(x = 1, y = 2), list(x = 3, y = 4))) }) test_that("warns if element too short", { x <- list(list(1, 2), list(1)) expect_warning(out <- transpose(x), "Element 2 has length 1") expect_equal(out, list(list(1, 1), list(2, NULL))) }) test_that("warns if element too long", { x <- list(list(1, 2), list(1, 2, 3)) expect_warning(out <- transpose(x), "Element 2 has length 3") expect_equal(out, list(list(1, 1), list(2, 2))) }) test_that("can transpose list of lists of atomic vectors", { x <- list(list(TRUE, 1L, 1, "1")) expect_equal(transpose(x), list(list(TRUE), list(1L), list(1), list("1"))) }) test_that("can transpose lists of atomic vectors", { expect_equal(transpose(list(TRUE, FALSE)), list(list(TRUE, FALSE))) expect_equal(transpose(list(1L, 2L)), list(list(1L, 2L))) expect_equal(transpose(list(1, 2)), list(list(1, 2))) expect_equal(transpose(list("a", "b")), list(list("a", "b"))) }) test_that("can't transpose expressions", { expect_error(transpose(list(expression(a))), "Unsupported type") }) # Named based matching ---------------------------------------------------- test_that("can override default names", { x <- list( list(x = 1), list(y = 2, x = 1) ) tx <- transpose(x, c("x", "y")) expect_equal(tx, list( x = list(1, 1), y = list(NULL, 2) )) }) test_that("if present, names are used", { x <- list( list(x = 1, y = 2), list(y = 2, x = 1) ) tx <- transpose(x) expect_equal(tx$x, list(1, 1)) expect_equal(tx$y, list(2, 2)) }) test_that("if missing elements, filled with NULL", { x <- list( list(x = 1, y = 2), list(x = 1) ) tx <- transpose(x) expect_equal(tx$y, list(2, NULL)) }) # Position based matching ------------------------------------------------- test_that("warning if too short", { x <- list( list(1, 2), list(1) ) expect_warning(tx <- transpose(x), "has length 1 not 2") expect_equal(tx, list(list(1, 1), list(2, NULL))) }) test_that("warning if too long", { x <- list( list(1), list(1, 2) ) expect_warning(tx <- transpose(x), "has length 2 not 1") expect_equal(tx, list(list(1, 1))) }) purrr/tests/testthat/test-every-some.R0000644000176200001440000000070713124217327017627 0ustar liggesuserscontext("every-some") test_that("return NA if present", { expect_equal(some(1:10, ~ NA), NA) expect_equal(every(1:10, ~ NA), NA) }) test_that("every returns TRUE if all elements are TRUE", { x <- list(0, 1, TRUE) expect_false(every(x, isTRUE)) expect_true(every(x[3], isTRUE)) }) test_that("some returns FALSE if all elements are FALSE", { x <- list(1, 0, FALSE) expect_false(some(x, isTRUE)) expect_true(some(x[1], negate(isTRUE))) }) purrr/tests/testthat/test-map2.R0000644000176200001440000000234013124217327016366 0ustar liggesuserscontext("map2") test_that("map2 inputs must be same length", { expect_error(map2(1:3, 2:3, function(...) NULL), "different lengths") }) test_that("map2 can't simplify if elements longer than length 1", { expect_error( map2_int(1:4, 5:8, range), "Result 1 is not a length 1 atomic vector" ) }) test_that("fails on non-vectors", { expect_error(map2(environment(), "a", identity), "not a vector") expect_error(map2("a", environment(), identity), "not a vector") }) test_that("map2 vectorised inputs of length 1", { expect_equal(map2(1:2, 1, `+`), list(2, 3)) expect_equal(map2(1, 1:2, `+`), list(2, 3)) }) test_that("any 0 length input gives 0 length output", { expect_equal(map2(list(), list(), ~ 1), list()) expect_equal(map2(1:10, list(), ~ 1), list()) expect_equal(map2(list(), 1:10, ~ 1), list()) expect_equal(map2(NULL, NULL, ~ 1), list()) expect_equal(map2(1:10, NULL, ~ 1), list()) expect_equal(map2(NULL, 1:10, ~ 1), list()) }) test_that("map2 takes only names from x", { x1 <- 1:3 x2 <- set_names(x1) expect_equal(names(map2(x1, x2, `+`)), NULL) expect_equal(names(map2(x2, x1, `+`)), names(x2)) }) test_that("map2 always returns a list", { expect_is(map2(mtcars, 0, ~mtcars), "list") }) purrr/tests/testthat/test-negate.R0000644000176200001440000000044213102346452016771 0ustar liggesuserscontext("negate") test_that("negate works with both functions and vectors", { true <- function(...) TRUE expect_equal(negate(true)(), FALSE) expect_equal(negate("x")(list(x = TRUE)), FALSE) expect_equal(negate(is.null)(TRUE), TRUE) expect_equal(negate(is.null)(NULL), FALSE) }) purrr/tests/testthat/test-modify.R0000644000176200001440000000332613124217327017023 0ustar liggesuserscontext("modify") test_that("modify returns same type as input", { df1 <- data.frame(x = 1:3, y = 4:6) expect_equal(modify(df1, length), data.frame(x = rep(3, 3), y = rep(3, 3))) }) test_that("modify_if/modify_at return same type as input", { df1 <- data.frame(x = "a", y = 2, stringsAsFactors = FALSE) exp <- data.frame(x = "A", y = 2, stringsAsFactors = FALSE) df2a <- modify_if(df1, is.character, toupper) expect_equal(df2a, exp) df2b <- modify_at(df1, "x", toupper) expect_equal(df2b, exp) }) test_that("modify_at requires a named object", { df1 <- data.frame(x = "a", y = 2, stringsAsFactors = FALSE) expect_error(modify_at(unname(df1), "x", toupper)) }) test_that("modify_at operates on character and numeric indexing", { df1 <- data.frame(x = "a", y = 2, stringsAsFactors = FALSE) expect_error(modify_at(df1, TRUE, toupper)) }) # modify_depth ------------------------------------------------------------ test_that("modify_depth modifies values at specified depth", { x1 <- list(list(list(1))) expect_equal(modify_depth(x1, 0, length), list(1)) expect_equal(modify_depth(x1, 1, length), list(1)) expect_equal(modify_depth(x1, 2, length), list(list(1))) expect_equal(modify_depth(x1, 3, length), list(list(list(1)))) expect_equal(modify_depth(x1, -1, length), list(list(list(1)))) expect_error(modify_depth(x1, 4, length), "List not deep enough") expect_error(modify_depth(x1, -5, length), "Invalid `depth`") }) test_that(".ragged = TRUE operates on leaves", { x1 <- list( list(1), list(list(2)) ) x2 <- list( list(2), list(list(3)) ) expect_equal(modify_depth(x1, 3, ~ . + 1, .ragged = TRUE), x2) expect_equal(modify_depth(x1, -1, ~ . + 1, .ragged = TRUE), x2) }) purrr/tests/testthat/test-flatten.R0000644000176200001440000000435313124217327017172 0ustar liggesuserscontext("flatten") test_that("input must be a list", { expect_error(flatten(1), "must be a list") }) test_that("contents of list must be supported types", { expect_error(flatten(list(quote(a))), "not a vector") expect_error(flatten(list(expression(a))), "Unsupported type") }) test_that("each second level element becomes first level element", { expect_equal(flatten(list(1:2)), list(1, 2)) expect_equal(flatten(list(1, 2)), list(1, 2)) }) test_that("can flatten all atomic vectors", { expect_equal(flatten(list(F)), list(F)) expect_equal(flatten(list(1L)), list(1L)) expect_equal(flatten(list(1)), list(1)) expect_equal(flatten(list("a")), list("a")) }) test_that("NULLs are silently dropped", { expect_equal(flatten(list(NULL, NULL)), list()) expect_equal(flatten(list(NULL, 1)), list(1)) expect_equal(flatten(list(1, NULL)), list(1)) }) test_that("names are preserved", { expect_equal(flatten(list(list(x = 1), list(y = 1))), list(x = 1, y = 1)) expect_equal(flatten(list(list(a = 1, b = 2), 3)), list(a = 1, b = 2, 3)) }) test_that("names of 'scalar' elements are preserved", { out <- flatten(list(a = list(1), b = list(2))) expect_equal(out, list(a = 1, b = 2)) out <- flatten(list(a = list(1), b = 2:3)) expect_equal(out, list(a = 1, 2, 3)) out <- flatten(list(list(a = 1, b = 2), c = 3)) expect_equal(out, list(a = 1, b = 2, c = 3)) }) test_that("child names beat parent names", { out <- flatten(list(a = list(x = 1), b = list(y = 2))) expect_equal(out, list(x = 1, y = 2)) }) # atomic flatten ---------------------------------------------------------- test_that("must be a list", { expect_error(flatten_lgl(1), "must be a list") }) test_that("can flatten all atomic vectors", { expect_equal(flatten_lgl(list(F)), F) expect_equal(flatten_int(list(1L)), 1L) expect_equal(flatten_dbl(list(1)), 1) expect_equal(flatten_chr(list("a")), "a") }) test_that("preserves inner names", { expect_equal( flatten_dbl(list(c(a = 1), c(b = 2))), c(a = 1, b = 2) ) }) # data frame flatten ------------------------------------------------------ test_that("can flatten to a data frame with named lists", { expect_is(flatten_dfr(list(c(a = 1), c(b = 2))), "data.frame") expect_error(flatten_dfc(list(1))) }) purrr/tests/testthat/test-map.R0000644000176200001440000000421013171452210016274 0ustar liggesuserscontext("map") test_that("preserves names", { out <- map(list(x = 1, y = 2), identity) expect_equal(names(out), c("x", "y")) }) test_that("creates simple call", { out <- map(1, function(x) sys.call())[[1]] expect_identical(out, quote(.f(.x[[i]], ...))) }) test_that("fails on non-vectors", { expect_error(map(environment(), identity), "not a vector") expect_error(map(quote(a), identity), "not a vector") }) test_that("0 length input gives 0 length output", { out1 <- map(list(), identity) expect_equal(out1, list()) out2 <- map(NULL, identity) expect_equal(out2, list()) }) test_that("map() always returns a list", { expect_is(map(mtcars, mean), "list") }) test_that("types automatically coerced upwards", { expect_identical(map_int(c(FALSE, TRUE), identity), c(0L, 1L)) expect_identical(map_dbl(c(FALSE, TRUE), identity), c(0, 1)) expect_identical(map_dbl(c(1L, 2L), identity), c(1, 2)) expect_identical(map_chr(c(FALSE, TRUE), identity), c("FALSE", "TRUE")) expect_identical(map_chr(c(1L, 2L), identity), c("1", "2")) expect_identical(map_chr(c(1.5, 2.5), identity), c("1.500000", "2.500000")) }) test_that("logical and integer NA become correct double NA", { expect_identical( map_dbl(list(NA, NA_integer_), identity), c(NA_real_, NA_real_) ) }) test_that("map forces arguments in same way as base R", { f_map <- map(1:2, function(i) function(x) x + i) f_base <- lapply(1:2, function(i) function(x) x + i) expect_equal(f_map[[1]](0), f_base[[1]](0)) expect_equal(f_map[[2]](0), f_base[[2]](0)) }) test_that("row and column binding work", { mtcar_mod <- mtcars %>% split(.$cyl) %>% map(~ lm(mpg ~ wt, data = .x)) f_coef <- function(x) as.data.frame(t(as.matrix(coef(x)))) expect_length(mtcar_mod %>% map_dfr(f_coef), 2) expect_length(mtcar_mod %>% map_dfc(f_coef), 6) }) test_that("walk is used for side-effects", { expect_output(walk(1:3, str)) }) test_that("map_if() and map_at() always return a list", { df <- tibble::tibble(x = 1, y = "a") expect_identical(map_if(df, is.character, ~"out"), list(x = 1, y = "out")) expect_identical(map_at(df, 1, ~"out"), list(x = "out", y = "a")) }) purrr/src/0000755000176200001440000000000013171650762012223 5ustar liggesuserspurrr/src/coerce.c0000644000176200001440000000502113171650762013625 0ustar liggesusers#define R_NO_REMAP #include #include #include double logical_to_real(int x) { return (x == NA_LOGICAL) ? NA_REAL : x; } double integer_to_real(int x) { return (x == NA_INTEGER) ? NA_REAL : x; } SEXP logical_to_char(int x) { if (x == NA_LOGICAL) return NA_STRING; return Rf_mkChar(x ? "TRUE" : "FALSE"); } SEXP integer_to_char(int x) { if (x == NA_INTEGER) return NA_STRING; char buf[100]; snprintf(buf, 100, "%d", x); return Rf_mkChar(buf); } SEXP double_to_char(double x) { if (!R_finite(x)) { if (R_IsNA(x)) { return NA_STRING; } else if (R_IsNaN(x)) { return Rf_mkChar("NaN"); } else if (x > 0) { return Rf_mkChar("Inf"); } else { return Rf_mkChar("-Inf"); } } char buf[100]; snprintf(buf, 100, "%f", x); return Rf_mkChar(buf); } void cant_coerce(SEXP from, SEXP to, int i) { Rf_errorcall(R_NilValue, "Can't coerce element %i from a %s to a %s", i + 1, Rf_type2char(TYPEOF(from)), Rf_type2char(TYPEOF(to))); } void set_vector_value(SEXP to, int i, SEXP from, int j) { switch(TYPEOF(to)) { case LGLSXP: switch(TYPEOF(from)) { case LGLSXP: LOGICAL(to)[i] = LOGICAL(from)[j]; break; default: cant_coerce(from, to, i); } break; case INTSXP: switch(TYPEOF(from)) { case LGLSXP: INTEGER(to)[i] = LOGICAL(from)[j]; break; case INTSXP: INTEGER(to)[i] = INTEGER(from)[j]; break; default: cant_coerce(from, to, i); } break; case REALSXP: switch(TYPEOF(from)) { case LGLSXP: REAL(to)[i] = logical_to_real(LOGICAL(from)[j]); break; case INTSXP: REAL(to)[i] = integer_to_real(INTEGER(from)[j]); break; case REALSXP: REAL(to)[i] = REAL(from)[j]; break; default: cant_coerce(from, to, i); } break; case STRSXP: switch(TYPEOF(from)) { case LGLSXP: SET_STRING_ELT(to, i, logical_to_char(LOGICAL(from)[j])); break; case INTSXP: SET_STRING_ELT(to, i, integer_to_char(INTEGER(from)[j])); break; case REALSXP: SET_STRING_ELT(to, i, double_to_char(REAL(from)[j])); break; case STRSXP: SET_STRING_ELT(to, i, STRING_ELT(from, j)); break; default: cant_coerce(from, to, i); } break; case VECSXP: SET_VECTOR_ELT(to, i, from); break; default: cant_coerce(from, to, i); } } SEXP coerce_impl(SEXP x, SEXP type_) { int n = Rf_length(x); SEXPTYPE type = Rf_str2type(CHAR(Rf_asChar(type_))); SEXP out = PROTECT(Rf_allocVector(type, n)); for (int i = 0; i < n; ++i) { set_vector_value(out, i, x, i); } UNPROTECT(1); return out; } purrr/src/backports.h0000644000176200001440000000024513171650762014365 0ustar liggesusers#ifndef BACKPORTS_H #define BACKPORTS_H #include #if defined(R_VERSION) && R_VERSION < R_Version(3, 2, 0) SEXP Rf_installChar(SEXP x); #endif #endif purrr/src/transpose.c0000644000176200001440000000553013171650762014410 0ustar liggesusers#define R_NO_REMAP #include #include SEXP transpose_impl(SEXP x, SEXP names_template) { if (TYPEOF(x) != VECSXP) Rf_errorcall(R_NilValue, "`.l` is not a list (%s)", Rf_type2char(TYPEOF(x))); int n = Rf_length(x); if (n == 0) { return Rf_allocVector(VECSXP, 0); } int has_template = !Rf_isNull(names_template); SEXP x1 = VECTOR_ELT(x, 0); if (!Rf_isVector(x1)) Rf_errorcall(R_NilValue, "Element 1 is not a vector (%s)", Rf_type2char(TYPEOF(x1))); int m = has_template ? Rf_length(names_template) : Rf_length(x1); // Create space for output SEXP out = PROTECT(Rf_allocVector(VECSXP, m)); SEXP names1 = PROTECT(Rf_getAttrib(x, R_NamesSymbol)); for (int j = 0; j < m; ++j) { SEXP xj = PROTECT(Rf_allocVector(VECSXP, n)); if (!Rf_isNull(names1)) { Rf_setAttrib(xj, R_NamesSymbol, names1); } SET_VECTOR_ELT(out, j, xj); UNPROTECT(1); } SEXP names2 = has_template ? names_template : Rf_getAttrib(x1, R_NamesSymbol); if (!Rf_isNull(names2)) { Rf_setAttrib(out, R_NamesSymbol, names2); } // Fill output for (int i = 0; i < n; ++i) { SEXP xi = VECTOR_ELT(x, i); if (!Rf_isVector(xi)) Rf_errorcall(R_NilValue, "Element %i is not a vector (%s)", i + 1, Rf_type2char(TYPEOF(x1))); // find mapping between names and index. Use -1 to indicate not found SEXP names_i = Rf_getAttrib(xi, R_NamesSymbol); SEXP index; if (!Rf_isNull(names2) && !Rf_isNull(names_i)) { index = PROTECT(Rf_match(names_i, names2, 0)); // Rf_match returns 1-based index; convert to 0-based for C for (int i = 0; i < m; ++i) { INTEGER(index)[i] = INTEGER(index)[i] - 1; } } else { index = PROTECT(Rf_allocVector(INTSXP, m)); int mi = Rf_length(xi); if (m != mi) { Rf_warningcall(R_NilValue, "Element %i has length %i not %i", i + 1, mi, m); } for (int i = 0; i < m; ++i) { INTEGER(index)[i] = (i < mi) ? i : -1; } } int* pIndex = INTEGER(index); for (int j = 0; j < m; ++j) { int pos = pIndex[j]; if (pos == -1) continue; switch(TYPEOF(xi)) { case LGLSXP: SET_VECTOR_ELT(VECTOR_ELT(out, j), i, Rf_ScalarLogical(LOGICAL(xi)[pos])); break; case INTSXP: SET_VECTOR_ELT(VECTOR_ELT(out, j), i, Rf_ScalarInteger(INTEGER(xi)[pos])); break; case REALSXP: SET_VECTOR_ELT(VECTOR_ELT(out, j), i, Rf_ScalarReal(REAL(xi)[pos])); break; case STRSXP: SET_VECTOR_ELT(VECTOR_ELT(out, j), i, Rf_ScalarString(STRING_ELT(xi, pos))); break; case VECSXP: SET_VECTOR_ELT(VECTOR_ELT(out, j), i, VECTOR_ELT(xi, pos)); break; default: Rf_errorcall(R_NilValue, "Unsupported type %s", Rf_type2char(TYPEOF(xi))); } } UNPROTECT(1); } UNPROTECT(2); return out; } purrr/src/backports.c0000644000176200001440000000031713171650762014360 0ustar liggesusers#define R_NO_REMAP #include #include #include #if defined(R_VERSION) && R_VERSION < R_Version(3, 2, 0) SEXP Rf_installChar(SEXP x) { return Rf_install(CHAR(x)); } #endif purrr/src/flatten.c0000644000176200001440000000706113171650762014030 0ustar liggesusers#define R_NO_REMAP #include #include #include "coerce.h" const char* objtype(SEXP x) { return Rf_type2char(TYPEOF(x)); } SEXP flatten_impl(SEXP x) { if (TYPEOF(x) != VECSXP) Rf_errorcall(R_NilValue, "`.x` must be a list (%s)", objtype(x)); int m = Rf_length(x); // Determine output size and check type int n = 0; int has_names = 0; SEXP x_names = PROTECT(Rf_getAttrib(x, R_NamesSymbol)); for (int j = 0; j < m; ++j) { SEXP x_j = VECTOR_ELT(x, j); if (!Rf_isVector(x_j) && !Rf_isNull(x_j)) Rf_errorcall(R_NilValue, "Element %i is not a vector (%s)", j + 1, objtype(x_j)); n += Rf_length(x_j); if (!has_names) { if (!Rf_isNull(Rf_getAttrib(x_j, R_NamesSymbol))) { // Sub-element is named has_names = 1; } else if (Rf_length(x_j) == 1 && !Rf_isNull(x_names)) { // Element is a "scalar" and has name in parent SEXP name = STRING_ELT(x_names, j); if (name != NA_STRING && strcmp(CHAR(name), "") != 0) has_names = 1; } } } SEXP out = PROTECT(Rf_allocVector(VECSXP, n)); SEXP names = PROTECT(Rf_allocVector(STRSXP, n)); if (has_names) Rf_setAttrib(out, R_NamesSymbol, names); int i = 0; for (int j = 0; j < m; ++j) { SEXP x_j = VECTOR_ELT(x, j); int n_j = Rf_length(x_j); SEXP names_j = PROTECT(Rf_getAttrib(x_j, R_NamesSymbol)); int has_names_j = !Rf_isNull(names_j); for (int k = 0; k < n_j; ++k, ++i) { switch(TYPEOF(x_j)) { case LGLSXP: SET_VECTOR_ELT(out, i, Rf_ScalarLogical(LOGICAL(x_j)[k])); break; case INTSXP: SET_VECTOR_ELT(out, i, Rf_ScalarInteger(INTEGER(x_j)[k])); break; case REALSXP: SET_VECTOR_ELT(out, i, Rf_ScalarReal(REAL(x_j)[k])); break; case STRSXP: SET_VECTOR_ELT(out, i, Rf_ScalarString(STRING_ELT(x_j, k))); break; case VECSXP: SET_VECTOR_ELT(out, i, VECTOR_ELT(x_j, k)); break; default: Rf_errorcall(R_NilValue, "Unsupported type at element %i (%s)", j + 1, objtype(x_j)); } if (has_names) { if (has_names_j) { SET_STRING_ELT(names, i, has_names_j ? STRING_ELT(names_j, k) : Rf_mkChar("")); } else if (n_j == 1) { SET_STRING_ELT(names, i, !Rf_isNull(x_names) ? STRING_ELT(x_names, j) : Rf_mkChar("")); } } if (i % 1000 == 0) R_CheckUserInterrupt(); } UNPROTECT(1); } UNPROTECT(3); return out; } SEXP vflatten_impl(SEXP x, SEXP type_) { if (TYPEOF(x) != VECSXP) Rf_errorcall(R_NilValue, "`.x` must be a list (%s)", objtype(x)); int m = Rf_length(x); SEXPTYPE type = Rf_str2type(CHAR(Rf_asChar(type_))); // Determine output size and type int n = 0; int has_names = 0; for (int j = 0; j < m; ++j) { SEXP x_j = VECTOR_ELT(x, j); n += Rf_length(x_j); if (!has_names && !Rf_isNull(Rf_getAttrib(x_j, R_NamesSymbol))) { has_names = 1; } } SEXP out = PROTECT(Rf_allocVector(type, n)); SEXP names = PROTECT(Rf_allocVector(STRSXP, n)); if (has_names) Rf_setAttrib(out, R_NamesSymbol, names); UNPROTECT(1); int i = 0; for (int j = 0; j < m; ++j) { SEXP x_j = VECTOR_ELT(x, j); int n_j = Rf_length(x_j); SEXP names_j = PROTECT(Rf_getAttrib(x_j, R_NamesSymbol)); int has_names_j = !Rf_isNull(names_j); for (int k = 0; k < n_j; ++k, ++i) { set_vector_value(out, i, x_j, k); if (has_names) SET_STRING_ELT(names, i, has_names_j ? STRING_ELT(names_j, k) : Rf_mkChar("")); if (i % 1000 == 0) R_CheckUserInterrupt(); } UNPROTECT(1); } UNPROTECT(1); return out; } purrr/src/map.h0000644000176200001440000000027313171650762013153 0ustar liggesusers#ifndef MAP_H #define MAP_H extern "C" { SEXP map_impl(SEXP env, SEXP x_name_, SEXP f_name_, SEXP type_); SEXP pmap_impl(SEXP env, SEXP l_name_, SEXP f_name_, SEXP type_); } #endif purrr/src/map.c0000644000176200001440000001357513171650762013157 0ustar liggesusers#define R_NO_REMAP #include #include #include #include "coerce.h" void copy_names(SEXP from, SEXP to) { if (Rf_length(from) != Rf_length(to)) return; SEXP names = Rf_getAttrib(from, R_NamesSymbol); if (Rf_isNull(names)) return; Rf_setAttrib(to, R_NamesSymbol, names); } // call must involve i SEXP call_loop(SEXP env, SEXP call, int n, SEXPTYPE type, int force_args) { // Create variable "i" and map to scalar integer SEXP i_val = PROTECT(Rf_ScalarInteger(1)); SEXP i = Rf_install("i"); Rf_defineVar(i, i_val, env); SEXP out = PROTECT(Rf_allocVector(type, n)); for (int i = 0; i < n; ++i) { if (i % 1000 == 0) R_CheckUserInterrupt(); INTEGER(i_val)[0] = i + 1; #if defined(R_VERSION) && R_VERSION >= R_Version(3, 2, 3) SEXP res = PROTECT(R_forceAndCall(call, force_args, env)); #else SEXP res = PROTECT(Rf_eval(call, env)); #endif if (type != VECSXP && Rf_length(res) != 1) Rf_errorcall(R_NilValue, "Result %i is not a length 1 atomic vector", i + 1); set_vector_value(out, i, res, 0); UNPROTECT(1); } UNPROTECT(2); return out; } SEXP map_impl(SEXP env, SEXP x_name_, SEXP f_name_, SEXP type_) { const char* x_name = CHAR(Rf_asChar(x_name_)); const char* f_name = CHAR(Rf_asChar(f_name_)); SEXP x = Rf_install(x_name); SEXP f = Rf_install(f_name); SEXP i = Rf_install("i"); SEXPTYPE type = Rf_str2type(CHAR(Rf_asChar(type_))); SEXP x_val = Rf_eval(x, env); if (Rf_isNull(x_val)) { return Rf_allocVector(type, 0); } else if (!Rf_isVector(x_val)) { Rf_errorcall(R_NilValue, "`.x` is not a vector (%s)", Rf_type2char(TYPEOF(x_val))); } int n = Rf_length(x_val); // Constructs a call like f(x[[i]], ...) - don't want to substitute // actual values for f or x, because they may be long, which creates // bad tracebacks() SEXP Xi = PROTECT(Rf_lang3(R_Bracket2Symbol, x, i)); SEXP f_call = PROTECT(Rf_lang3(f, Xi, R_DotsSymbol)); SEXP out = PROTECT(call_loop(env, f_call, n, type, 1)); copy_names(x_val, out); UNPROTECT(3); return out; } SEXP map2_impl(SEXP env, SEXP x_name_, SEXP y_name_, SEXP f_name_, SEXP type_) { const char* x_name = CHAR(Rf_asChar(x_name_)); const char* y_name = CHAR(Rf_asChar(y_name_)); const char* f_name = CHAR(Rf_asChar(f_name_)); SEXP x = Rf_install(x_name); SEXP y = Rf_install(y_name); SEXP f = Rf_install(f_name); SEXP i = Rf_install("i"); SEXPTYPE type = Rf_str2type(CHAR(Rf_asChar(type_))); SEXP x_val = PROTECT(Rf_eval(x, env)); SEXP y_val = PROTECT(Rf_eval(y, env)); if (!Rf_isVector(x_val) && !Rf_isNull(x_val)) Rf_errorcall(R_NilValue, "`.x` is not a vector (%s)", Rf_type2char(TYPEOF(x_val))); if (!Rf_isVector(y_val) && !Rf_isNull(y_val)) Rf_errorcall(R_NilValue, "`.y` is not a vector (%s)", Rf_type2char(TYPEOF(y_val))); int nx = Rf_length(x_val), ny = Rf_length(y_val); if (nx == 0 || ny == 0) { UNPROTECT(2); return Rf_allocVector(type, 0); } if (nx != ny && !(nx == 1 || ny == 1)) { Rf_errorcall(R_NilValue, "`.x` (%i) and `.y` (%i) are different lengths", nx, ny); } int n = (nx > ny) ? nx : ny; // Constructs a call like f(x[[i]], y[[i]], ...) SEXP one = PROTECT(Rf_ScalarInteger(1)); SEXP Xi = PROTECT(Rf_lang3(R_Bracket2Symbol, x, nx == 1 ? one : i)); SEXP Yi = PROTECT(Rf_lang3(R_Bracket2Symbol, y, ny == 1 ? one : i)); SEXP f_call = PROTECT(Rf_lang4(f, Xi, Yi, R_DotsSymbol)); SEXP out = PROTECT(call_loop(env, f_call, n, type, 2)); copy_names(x_val, out); UNPROTECT(7); return out; } SEXP pmap_impl(SEXP env, SEXP l_name_, SEXP f_name_, SEXP type_) { const char* l_name = CHAR(Rf_asChar(l_name_)); SEXP l = Rf_install(l_name); SEXP l_val = PROTECT(Rf_eval(l, env)); SEXPTYPE type = Rf_str2type(CHAR(Rf_asChar(type_))); if (!Rf_isVectorList(l_val)) Rf_errorcall(R_NilValue, "`.x` is not a list (%s)", Rf_type2char(TYPEOF(l_val))); // Check all elements are lists and find maximum length int m = Rf_length(l_val); int n = 0; for (int j = 0; j < m; ++j) { SEXP j_val = VECTOR_ELT(l_val, j); if (!Rf_isVector(j_val) && !Rf_isNull(j_val)) { Rf_errorcall(R_NilValue, "Element %i is not a vector (%s)", j + 1, Rf_type2char(TYPEOF(j_val))); } int nj = Rf_length(j_val); if (nj == 0) { UNPROTECT(1); return Rf_allocVector(type, 0); } else if (nj > n) { n = nj; } } // Check length of all elements for (int j = 0; j < m; ++j) { SEXP j_val = VECTOR_ELT(l_val, j); int nj = Rf_length(j_val); if (nj != 1 && nj != n) Rf_errorcall(R_NilValue, "Element %i has length %i, not 1 or %i.", j + 1, nj, n); } SEXP l_names = PROTECT(Rf_getAttrib(l_val, R_NamesSymbol)); int has_names = !Rf_isNull(l_names); const char* f_name = CHAR(Rf_asChar(f_name_)); SEXP f = Rf_install(f_name); SEXP i = Rf_install("i"); SEXP one = PROTECT(Rf_ScalarInteger(1)); // Construct call like f(.x[[c(1, i)]], .x[[c(2, i)]], ...) // We construct the call backwards because can only add to the front of a // linked list. That makes PROTECTion tricky because we need to update it // each time to point to the start of the linked list. SEXP f_call = Rf_lang1(R_DotsSymbol); PROTECT_INDEX fi; PROTECT_WITH_INDEX(f_call, &fi); for (int j = m - 1; j >= 0; --j) { int nj = Rf_length(VECTOR_ELT(l_val, j)); // Construct call like .l[[c(j, i)]] SEXP j_ = PROTECT(Rf_ScalarInteger(j + 1)); SEXP ji_ = PROTECT(Rf_lang3(Rf_install("c"), j_, nj == 1 ? one : i)); SEXP l_ji = PROTECT(Rf_lang3(R_Bracket2Symbol, l, ji_)); REPROTECT(f_call = Rf_lcons(l_ji, f_call), fi); if (has_names && CHAR(STRING_ELT(l_names, j))[0] != '\0') SET_TAG(f_call, Rf_install(CHAR(STRING_ELT(l_names, j)))); UNPROTECT(3); } REPROTECT(f_call = Rf_lcons(f, f_call), fi); SEXP out = PROTECT(call_loop(env, f_call, n, type, m)); copy_names(VECTOR_ELT(l_val, 0), out); UNPROTECT(5); return out; } purrr/src/init.c0000644000176200001440000000210513171650762013330 0ustar liggesusers#include #include #include // for NULL #include /* .Call calls */ extern SEXP coerce_impl(SEXP, SEXP); extern SEXP extract_impl(SEXP, SEXP, SEXP); extern SEXP flatten_impl(SEXP); extern SEXP map_impl(SEXP, SEXP, SEXP, SEXP); extern SEXP map2_impl(SEXP, SEXP, SEXP, SEXP, SEXP); extern SEXP pmap_impl(SEXP, SEXP, SEXP, SEXP); extern SEXP transpose_impl(SEXP, SEXP); extern SEXP vflatten_impl(SEXP, SEXP); static const R_CallMethodDef CallEntries[] = { {"coerce_impl", (DL_FUNC) &coerce_impl, 2}, {"extract_impl", (DL_FUNC) &extract_impl, 3}, {"flatten_impl", (DL_FUNC) &flatten_impl, 1}, {"map_impl", (DL_FUNC) &map_impl, 4}, {"map2_impl", (DL_FUNC) &map2_impl, 5}, {"pmap_impl", (DL_FUNC) &pmap_impl, 4}, {"transpose_impl", (DL_FUNC) &transpose_impl, 2}, {"vflatten_impl", (DL_FUNC) &vflatten_impl, 2}, {NULL, NULL, 0} }; void R_init_purrr(DllInfo *dll) { R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); R_useDynamicSymbols(dll, FALSE); } purrr/src/coerce.h0000644000176200001440000000025113171650762013632 0ustar liggesusers#ifndef UTILS_H #define UTILS_H // Set value of to[i] to from[j], coercing vectors using usual rules. void set_vector_value(SEXP to, int i, SEXP from, int j); #endif purrr/src/extract.c0000644000176200001440000000756713171650762014060 0ustar liggesusers#define R_NO_REMAP #include #include #include "coerce.h" #include "backports.h" #include int find_offset(SEXP x, SEXP index, int i) { if (Rf_length(index) > 1) { Rf_errorcall(R_NilValue, "Index %i must have length 1", i + 1); } int n = Rf_length(x); if (n == 0) return -1; if (TYPEOF(index) == INTSXP) { int val = INTEGER(index)[0]; if (val == NA_INTEGER) return -1; val--; if (val < 0 || val >= n) return -1; return val; } if (TYPEOF(index) == REALSXP) { double val = REAL(index)[0]; if (!R_finite(val)) return -1; val--; if (val < 0 || val >= n) return -1; return val; } else if (TYPEOF(index) == STRSXP) { SEXP names = PROTECT(Rf_getAttrib(x, R_NamesSymbol)); if (names == R_NilValue) {// vector doesn't have names UNPROTECT(1); return -1; } if (STRING_ELT(index, 0) == NA_STRING) { UNPROTECT(1); return -1; } const char* val = Rf_translateCharUTF8(STRING_ELT(index, 0)); if (val[0] == '\0') { // "" matches nothing UNPROTECT(1); return -1; } for (int j = 0; j < Rf_length(names); ++j) { if (STRING_ELT(names, j) == NA_STRING) continue; const char* names_j = Rf_translateCharUTF8(STRING_ELT(names, j)); if (strcmp(names_j, val) == 0) { UNPROTECT(1); return j; } } UNPROTECT(1); return -1; } else { Rf_errorcall( R_NilValue, "Index %i must be a character or numeric vector", i + 1 ); } } SEXP extract_vector(SEXP x, SEXP index_i, int i) { int offset = find_offset(x, index_i, i); if (offset < 0) return R_NilValue; switch(TYPEOF(x)) { case LGLSXP: return Rf_ScalarLogical(LOGICAL(x)[offset]); case INTSXP: return Rf_ScalarInteger(INTEGER(x)[offset]); case REALSXP: return Rf_ScalarReal(REAL(x)[offset]); case STRSXP: return Rf_ScalarString(STRING_ELT(x, offset)); case VECSXP: return VECTOR_ELT(x, offset); default: Rf_errorcall(R_NilValue, "Don't know how to index object of type %s at level %i", Rf_type2char(TYPEOF(x)), i + 1 ); } return R_NilValue; } SEXP extract_env(SEXP x, SEXP index_i, int i) { if (TYPEOF(index_i) != STRSXP || Rf_length(index_i) != 1) { Rf_errorcall(R_NilValue, "Index %i is not a string", i + 1); } SEXP index = STRING_ELT(index_i, 0); if (index == NA_STRING) return R_NilValue; SEXP sym = Rf_installChar(index); SEXP out = Rf_findVarInFrame3(x, sym, TRUE); return (out == R_UnboundValue) ? R_NilValue : out; } SEXP extract_attr(SEXP x, SEXP index_i, int i) { if (TYPEOF(index_i) != STRSXP || Rf_length(index_i) != 1) { Rf_errorcall(R_NilValue, "Index %i is not a string", i + 1); } SEXP index = STRING_ELT(index_i, 0); if (index == NA_STRING) return R_NilValue; SEXP sym = Rf_installChar(index); return Rf_getAttrib(x, sym); } SEXP extract_clo(SEXP x, SEXP clo) { SEXP expr = PROTECT(Rf_lang2(clo, x)); SEXP out = Rf_eval(expr, R_EmptyEnv); UNPROTECT(1); return out; } SEXP extract_impl(SEXP x, SEXP index, SEXP missing) { if (TYPEOF(index) != VECSXP) { Rf_errorcall(R_NilValue, "`index` must be a list (not a %s)", Rf_type2char(TYPEOF(index))); } int n = Rf_length(index); for (int i = 0; i < n; ++i) { SEXP index_i = VECTOR_ELT(index, i); if (TYPEOF(index_i) == CLOSXP) { x = extract_clo(x, index_i); } else { if (Rf_isNull(x)) { return missing; } else if (Rf_isVector(x)) { x = extract_vector(x, index_i, i); } else if (Rf_isEnvironment(x)) { x = extract_env(x, index_i, i); } else if (Rf_isS4(x)) { x = extract_attr(x, index_i, i); } else { Rf_errorcall(R_NilValue, "Don't know how to pluck from a %s", Rf_type2char(TYPEOF(x)) ); } } } return (Rf_length(x) == 0) ? missing : x; } purrr/NAMESPACE0000644000176200001440000000603413137651733012657 0ustar liggesusers# Generated by roxygen2: do not edit by hand S3method(as_mapper,character) S3method(as_mapper,default) S3method(as_mapper,list) S3method(as_mapper,numeric) S3method(modify,default) S3method(modify_at,default) S3method(modify_depth,default) S3method(modify_if,default) export("%>%") export("%@%") export("%||%") export(accumulate) export(accumulate_right) export(array_branch) export(array_tree) export(as_function) export(as_mapper) export(as_vector) export(at_depth) export(attr_getter) export(auto_browse) export(compact) export(compose) export(cross) export(cross2) export(cross3) export(cross_d) export(cross_df) export(cross_n) export(detect) export(detect_index) export(discard) export(every) export(flatten) export(flatten_chr) export(flatten_dbl) export(flatten_df) export(flatten_dfc) export(flatten_dfr) export(flatten_int) export(flatten_lgl) export(has_element) export(head_while) export(imap) export(imap_chr) export(imap_dbl) export(imap_dfc) export(imap_dfr) export(imap_int) export(imap_lgl) export(invoke) export(invoke_map) export(invoke_map_chr) export(invoke_map_dbl) export(invoke_map_df) export(invoke_map_dfc) export(invoke_map_dfr) export(invoke_map_int) export(invoke_map_lgl) export(is_atomic) export(is_bare_atomic) export(is_bare_character) export(is_bare_double) export(is_bare_integer) export(is_bare_list) export(is_bare_logical) export(is_bare_numeric) export(is_bare_vector) export(is_character) export(is_double) export(is_empty) export(is_formula) export(is_function) export(is_integer) export(is_list) export(is_logical) export(is_null) export(is_numeric) export(is_scalar_atomic) export(is_scalar_character) export(is_scalar_double) export(is_scalar_integer) export(is_scalar_list) export(is_scalar_logical) export(is_scalar_numeric) export(is_scalar_vector) export(is_vector) export(iwalk) export(keep) export(lift) export(lift_dl) export(lift_dv) export(lift_ld) export(lift_lv) export(lift_vd) export(lift_vl) export(list_along) export(list_merge) export(list_modify) export(lmap) export(lmap_at) export(lmap_if) export(map) export(map2) export(map2_chr) export(map2_dbl) export(map2_df) export(map2_dfc) export(map2_dfr) export(map2_int) export(map2_lgl) export(map_at) export(map_call) export(map_chr) export(map_dbl) export(map_df) export(map_dfc) export(map_dfr) export(map_if) export(map_int) export(map_lgl) export(modify) export(modify_at) export(modify_depth) export(modify_if) export(negate) export(partial) export(pluck) export(pmap) export(pmap_chr) export(pmap_dbl) export(pmap_df) export(pmap_dfc) export(pmap_dfr) export(pmap_int) export(pmap_lgl) export(possibly) export(prepend) export(pwalk) export(quietly) export(rbernoulli) export(rdunif) export(reduce) export(reduce2) export(reduce2_right) export(reduce_right) export(rep_along) export(rerun) export(safely) export(set_names) export(simplify) export(simplify_all) export(some) export(splice) export(tail_while) export(transpose) export(update_list) export(vec_depth) export(walk) export(walk2) export(when) import(rlang) importFrom(magrittr,"%>%") useDynLib(purrr, .registration = TRUE) purrr/NEWS.md0000644000176200001440000003006613171650736012540 0ustar liggesusers# purrr 0.2.4 * Fixes for R 3.1. * Fix PROTECTion problems in C code revealed by rchk. # purrr 0.2.3 ## Breaking changes We noticed the following issues during reverse dependencies checks: * If `reduce()` fails with this message: ``Error: `.x` is empty, and no `.init` supplied``, this is because `reduce()` now returns `.init` when `.x` is empty. Fix the problem by supplying an appropriate argument to `.init`, or by providing special behaviour when `.x` has length 0. * The type predicates have been migrated to rlang. Consequently the `bare-type-predicates` documentation topic is no longer in purrr, which might cause a warning if you cross-reference it. ## Dependencies purrr no longer depends on lazyeval or Rcpp (or dplyr, as of the previous version). This makes the dependency graph of the tidyverse simpler, and makes purrr more suitable as a dependency of lower-level packages. There have also been two changes to eliminate name conflicts between purrr and dplyr: * `order_by()`, `sort_by()` and `split_by()` have been removed. `order_by()` conflicted with `dplyr::order_by()` and the complete family doesn't feel that useful. Use tibbles instead (#217). * `contains()` has been renamed to `has_element()` to avoid conflicts with dplyr (#217). ## pluck() The plucking mechanism used for indexing into data structures with `map()` has been extracted into the function `pluck()`. Plucking is often more readable to extract an element buried in a deep data structure. Compare this syntax-heavy extraction which reads non-linearly: ``` accessor(x[[1]])$foo ``` to the equivalent pluck: ``` x %>% pluck(1, accessor, "foo") ``` ## Map helpers * `as_function()` is now `as_mapper()` because it is a tranformation that makes sense primarily for mapping functions, not in general (#298). `.null` has been renamed to `.default` to better reflect its intent (#298). `.default` is returned whenever an element is absent or empty (#231, #254). `as_mapper()` sanitises primitive functions by transforming them to closures with standardised argument names (using `rlang::as_closure()`). For instance `+` is transformed to `function(.x, .y) .x + .y`. This results in proper argument matching so that `map(1:10, partial(`-`, .x = 5))` produces `list(5 - 1, 5 - 2, ...)`. * Recursive indexing can now extract objects out of environments (#213) and S4 objects (#200), as well as lists. * `attr_getter()` makes it possible to extract from attributes like `map(list(iris, mtcars), attr_getter("row.names"))`. * The argument list for formula-functions has been tweaked so that you can refer to arguments by position with `..1`, `..2`, and so on. This makes it possible to use the formula shorthand for functions with more than two arguments (#289). * `possibly()`, `safely()` and friends no longer capture interrupts: this means that you can now terminate a mapper using one of these with Escape or Ctrl + C (#314) ## Map functions * All map functions now treat `NULL` the same way as an empty vector (#199), and return an empty vector if any input is an empty vector. * All `map()` functions now force their arguments in the same way that base R does for `lapply()` (#191). This makes `map()` etc easier to use when generating functions. * A new family of "indexed" map functions, `imap()`, `imap_lgl()` etc, provide a short-hand for `map2(x, names(x))` or `map2(x, seq_along(x))` (#240). * The data frame suffix `_df` has been (soft) deprecated in favour of `_dfr` to more clearly indicate that it's a row-bind. All variants now also have a `_dfc` for column binding (#167). (These will not be terribly useful until `dplyr::bind_rows()`/`dplyr::bind_cols()` have better semantics for vectors.) ## Modify functions A new `modify()` family returns the same output of the type as the input `.x`. This is in contrast to the `map()` family which always returns a list, regardless of the input type. The modify functions are S3 generics. However their default methods should be sufficient for most classes since they rely on the semantics of `[<-`. `modify.default()` is thus a shorthand for `x[] <- map(x, f)`. * `at_depth()` has been renamed to `modify_depth()`. * `modify_depth()` gains new `.ragged` argument, and negative depths are now computed relative to the deepest component of the list (#236). ## New functions * `auto_browse(f)` returns a new function that automatically calls `browser()` if `f` throws an error (#281). * `vec_depth()` computes the depth (i.e. the number of levels of indexing) or a vector (#243). * `reduce2()` and `reduce2_right()` make it possible to reduce with a 3 argument function where the first argument is the accumulated value, the second argument is `.x`, and the third argument is `.y` (#163). * `list_modify()` extends `stats::modifyList()` to replace by position if the list is not named.(#201). `list_merge()` operates similarly to `list_modify()` but combines instead of replacing (#322). * The legacy function `update_list()` is basically a version of `list_modify` that evaluates formulas within the list. It is likely to be deprecated in the future in favour of a tidyeval interface such as a list method for `dplyr::mutate()`. ## Minor improvements and bug fixes * Thanks to @dchiu911, the unit test coverage of purrr is now much greater. * All predicate functions are re-exported from rlang (#124). * `compact()` now works with standard mapper conventions (#282). * `cross_n()` has been renamed to `cross()`. The `_n` suffix was removed for consistency with `pmap()` (originally called `map_n()` at the start of the project) and `transpose()` (originally called `zip_n()`). Similarly, `cross_d()` has been renamed to `cross_df()` for consistency with `map_df()`. * `every()` and `some()` now return `NA` if present in the input (#174). * `invoke()` uses a more robust approach to generate the argument list (#249) It no longer uses lazyeval to figure out which enviroment a character `f` comes from. * `is_numeric()` and `is_scalar_numeric()` are deprecated because they don't test for what you might expect at first sight. * `reduce()` now throws an error if `.x` is empty and `.init` is not supplied. * Deprecated functions `flatmap()`, `map3()`, `map_n()`, `walk3()`, `walk_n()`, `zip2()`, `zip3()`, `zip_n()` have been removed. * `pmap()` coerces data frames to lists to avoid the expensive `[.data.frame` which provides security that is unneeded here (#220). * `rdunif()` checks its inputs for validity (#211). * `set_names()` can now take a function to tranform the names programmatically (#276), and you can supply names in `...` to reduce typing even more more (#316). `set_names()` is now powered by `rlang::set_names()`. * `safely()` now actually uses the `quiet` argument (#296). * `transpose()` now matches by name if available (#164). You can override the default choice with the new `.names` argument. * The function argument of `detect()` and `detect_index()` have been renamed from `.p` to `.f`. This is because they have mapper semantics rather than predicate semantics. # purrr 0.2.2.1 This is a compatibility release with dplyr 0.6.0. * All data-frame based mappers have been removed in favour of new functions and idioms in the tidyverse. `dmap()`, `dmap_at()`, `dmap_if()`, `invoke_rows()`, `slice_rows()`, `map_rows()`, `by_slice()`, `by_row()`, and `unslice()` have been moved to purrrlyr. This is a bit of an aggresive change but it allows us to make the dependencies much lighter. # purrr 0.2.2 * Fix for dev tibble support. * `as_function()` now supports list arguments which allow recursive indexing using either names or positions. They now always stop when encountering the first NULL (#173). * `accumulate` and `reduce` correctly pass extra arguments to the worker function. # purrr 0.2.1 * `as_function()` gains a `.null` argument that for character and numeric values allows you to specify what to return for null/absent elements (#110). This can be used with any map function, e.g. `map_int(x, 1, .null = NA)` * `as_function()` is now generic. * New `is_function()` that returns `TRUE` only for regular functions. * Fix crash on GCC triggered by `invoke_rows()`. # purrr 0.2.0 ## New functions * There are two handy infix functions: * `x %||% y` is shorthand for `if (is.null(x)) y else x` (#109). * `x %@% "a"` is shorthand for `attr(x, "a", exact = TRUE)` (#69). * `accumulate()` has been added to handle recursive folding. It is shortand for `Reduce(f, .x, accumulate = TRUE)` and follows a similar syntax to `reduce()` (#145). A right-hand version `accumulate_right()` was also added. * `map_df()` row-binds output together. It's the equivalent of `plyr::ldply()` (#127) * `flatten()` is now type-stable and always returns a list. To return a simpler vector, use `flatten_lgl()`, `flatten_int()`, `flatten_dbl()`, `flatten_chr()`, or `flatten_df()`. * `invoke()` has been overhauled to be more useful: it now works similarly to `map_call()` when `.x` is NULL, and hence `map_call()` has been deprecated. `invoke_map()` is a vectorised complement to `invoke()` (#125), and comes with typed variants `invoke_map_lgl()`, `invoke_map_int()`, `invoke_map_dbl()`, `invoke_map_chr()`, and `invoke_map_df()`. * `transpose()` replaces `zip2()`, `zip3()`, and `zip_n()` (#128). The name more clearly reflects the intent (transposing the first and second levels of list). It no longer has fields argument or the `.simplify` argument; instead use the new `simplify_all()` function. * `safely()`, `quietly()`, and `possibly()` are experimental functions for working with functions with side-effects (e.g. printed output, messages, warnings, and errors) (#120). `safely()` is a version of `try()` that modifies a function (rather than an expression), and always returns a list with two components, `result` and `error`. * `list_along()` and `rep_along()` generalise the idea of `seq_along()`. (#122). * `is_null()` is the snake-case version of `is.null()`. * `pmap()` (parallel map) replaces `map_n()` (#132), and has typed-variants suffixed `pmap_lgl()`, `pmap_int()`, `pmap_dbl()`, `pmap_chr()`, and `pmap_df()`. * `set_names()` is a snake-case alternative to `setNames()` with stricter equality checking, and more convenient defaults for pipes: `x %>% set_names()` is equivalent to `setNames(x, x)` (#119). ## Row based functionals We are still figuring out what belongs in dplyr and what belongs in purrr. Expect much experimentation and many changes with these functions. * `map()` now always returns a list. Data frame support has been moved to `map_df()` and `dmap()`. The latter supports sliced data frames as a shortcut for the combination of `by_slice()` and `dmap()`: `x %>% by_slice(dmap, fun, .collate = "rows")`. The conditional variants `dmap_at()` and `dmap_if()` also support sliced data frames and will recycle scalar results to the slice size. * `map_rows()` has been renamed to `invoke_rows()`. As other rows-based functionals, it collates results inside lists by default, but with column collation this function is equivalent to `plyr::mdply()`. * The rows-based functionals gain a `.to` option to name the output column as well as a `.collate` argument. The latter allows to collate the output in lists (by default), on columns or on rows. This makes these functions more flexible and more predictable. ## Bug fixes and minor changes * `as_function()`, which converts formulas etc to functions, is now exported (#123). * `rerun()` is correctly scoped (#95) * `update_list()` can now modify an element called `x` (#98). * `map*()` now use custom C code, rather than relying on `lapply()`, `mapply()` etc. The performance characteristcs are very similar, but it allows us greater control over the output (#118). * `map_lgl()` now has second argument `.f`, not `.p` (#134). ## Deprecated functions * `flatmap()` -> use `map()` followed by the appropriate `flatten()`. * `map_call()` -> `invoke()`. * `map_n()` -> `pmap()`; `walk_n()` -> `pwalk()`. * `map3(x, y, z)` -> `map_n(list(x, y, z))`; `walk3(x, y, z) -> `pwalk(list(x, y, z))` purrr/R/0000755000176200001440000000000013171452210011621 5ustar liggesuserspurrr/R/coerce.R0000644000176200001440000000047513102444774013224 0ustar liggesusers# Used internally by map and flatten. # Exposed here for testing coerce <- function(x, type) { .Call(coerce_impl, x, type) } coerce_lgl <- function(x) coerce(x, "logical") coerce_int <- function(x) coerce(x, "integer") coerce_dbl <- function(x) coerce(x, "double") coerce_chr <- function(x) coerce(x, "character") purrr/R/modify.R0000644000176200001440000001307413137651733013255 0ustar liggesusers#' Modify elements selectively #' #' `modify()` is a short-cut for `x[] <- map(x, .f); return(x)`. `modify_if()` #' only modifies the elements of `x` that satisfy a predicate and leaves the #' others unchanged. `modify_at()` only modifies elements given by names or #' positions. `modify_depth()` only modifies elements at a given level of a #' nested data structure. #' #' Since the transformation can alter the structure of the input; it's #' your responsibility to ensure that the transformation produces a #' valid output. For example, if you're modifying a data frame, `.f` #' must preserve the length of the input. #' #' #' @section Genericity: #' #' All these functions are S3 generic. However, the default method is #' sufficient in many cases. It should be suitable for any data type #' that implements the subset-assignment method `[<-`. #' #' In some cases it may make sense to provide a custom implementation #' with a method suited to your S3 class. For example, a `grouped_df` #' method might take into account the grouped nature of a data frame. #' #' @inheritParams map #' @param .depth Level of `.x` to map on. Use a negative value to count up #' from the lowest level of the list. #' #' * `modify_depth(x, 0, fun)` is equivalent to `x[] <- fun(x)` #' * `modify_depth(x, 1, fun)` is equivalent to `x[] <- map(x, fun)` #' * `modify_depth(x, 2, fun)` is equivalent to `x[] <- map(x, ~ map(., fun))` #' @return An object the same class as `.x` #' @family map variants #' @export #' @examples #' # Convert factors to characters #' iris %>% #' modify_if(is.factor, as.character) %>% #' str() #' #' # Specify which columns to map with a numeric vector of positions: #' mtcars %>% modify_at(c(1, 4, 5), as.character) %>% str() #' #' # Or with a vector of names: #' mtcars %>% modify_at(c("cyl", "am"), as.character) %>% str() #' #' list(x = rbernoulli(100), y = 1:100) %>% #' transpose() %>% #' modify_if("x", ~ update_list(., y = ~ y * 100)) %>% #' transpose() %>% #' simplify_all() #' #' # Modify at specified depth --------------------------- #' l1 <- list( #' obj1 = list( #' prop1 = list(param1 = 1:2, param2 = 3:4), #' prop2 = list(param1 = 5:6, param2 = 7:8) #' ), #' obj2 = list( #' prop1 = list(param1 = 9:10, param2 = 11:12), #' prop2 = list(param1 = 12:14, param2 = 15:17) #' ) #' ) #' #' # In the above list, "obj" is level 1, "prop" is level 2 and "param" #' # is level 3. To apply sum() on all params, we map it at depth 3: #' l1 %>% modify_depth(3, sum) %>% str() #' #' # modify() lets us pluck the elements prop1/param2 in obj1 and obj2: #' l1 %>% modify(c("prop1", "param2")) %>% str() #' #' # But what if we want to pluck all param2 elements? Then we need to #' # act at a lower level: #' l1 %>% modify_depth(2, "param2") %>% str() #' #' # modify_depth() can be with other purrr functions to make them operate at #' # a lower level. Here we ask pmap() to map paste() simultaneously over all #' # elements of the objects at the second level. paste() is effectively #' # mapped at level 3. #' l1 %>% modify_depth(2, ~ pmap(., paste, sep = " / ")) %>% str() modify <- function(.x, .f, ...) { UseMethod("modify") } #' @rdname modify #' @export modify.default <- function(.x, .f, ...) { .x[] <- map(.x, .f, ...) .x } #' @rdname modify #' @export modify_if <- function(.x, .p, .f, ...) { UseMethod("modify_if") } #' @rdname modify #' @export modify_if.default <- function(.x, .p, .f, ...) { sel <- probe(.x, .p) .x[sel] <- map(.x[sel], .f, ...) .x } #' @rdname modify #' @export modify_at <- function(.x, .at, .f, ...) { UseMethod("modify_at") } #' @rdname modify #' @export modify_at.default <- function(.x, .at, .f, ...) { sel <- inv_which(.x, .at) .x[sel] <- map(.x[sel], .f, ...) .x } #' @rdname modify #' @export #' @param .ragged If `TRUE`, will apply to leaves, even if they're not #' at depth `.depth`. If `FALSE`, will throw an error if there are #' no elements at depth `.depth`. modify_depth <- function(.x, .depth, .f, ..., .ragged = .depth < 0) { UseMethod("modify_depth") } #' @rdname modify #' @export modify_depth.default <- function(.x, .depth, .f, ..., .ragged = .depth < 0) { stopifnot(is_integerish(.depth, n = 1)) if (.depth < 0) { .depth <- vec_depth(.x) + .depth } .f <- as_mapper(.f, ...) modify_depth_rec(.x, .depth, .f, ..., .ragged = .ragged) } modify_depth_rec <- function(.x, .depth, .f, ..., .ragged = FALSE) { if (.depth == 0) { .x[] <- .f(.x, ...) } else if (.depth == 1) { if (!is.list(.x)) { if (.ragged) { .x[] <- .f(.x, ...) } else { stop("List not deep enough", call. = FALSE) } } else { .x[] <- map(.x, .f, ...) } } else if (.depth > 1) { .x[] <- map(.x, function(x) { modify_depth_rec(x, .depth - 1, .f, ..., .ragged = .ragged) }) } else { stop("Invalid `depth`", call. = FALSE) } .x } #' @export #' @usage NULL #' @rdname modify at_depth <- function(.x, .depth, .f, ...) { warning( "at_depth() is deprecated, please use `modify_depth()` instead", call. = FALSE ) modify_depth(.x, .depth, .f, ...) } # Internal version of map_lgl() that works with logical vectors probe <- function(.x, .p, ...) { if (is_logical(.p)) { stopifnot(length(.p) == length(.x)) .p } else { map_lgl(.x, .p, ...) } } inv_which <- function(x, sel) { if (is.character(sel)) { names <- names(x) if (is.null(names)) { stop("character indexing requires a named object", call. = FALSE) } names %in% sel } else if (is.numeric(sel)) { seq_along(x) %in% sel } else { stop("unrecognised index type", call. = FALSE) } } purrr/R/along.R0000644000176200001440000000113313171415127013050 0ustar liggesusers#' Helper to create vectors with matching length. #' #' These functions take the idea of [seq_along()] and generalise #' it to creating lists (`list_along`) and repeating values #' (`rep_along`). #' #' @param x A vector. #' @param y Values to repeat. #' @return A vector of the same length as `x`. #' @keywords internal #' @examples #' x <- 1:5 #' rep_along(x, 1:2) #' rep_along(x, 1) #' list_along(x) #' @name along NULL #' @export #' @rdname along list_along <- function(x) { vector("list", length(x)) } #' @export #' @rdname along rep_along <- function(x, y) { rep(y, length.out = length(x)) } purrr/R/as_mapper.R0000644000176200001440000001464013171451356013732 0ustar liggesusers#' Convert an object into a mapper function #' #' `as_mapper` is the powerhouse behind the varied function #' specifications that most purrr functions allow. It is an S3 #' generic. The default method forwards its arguments to #' [rlang::as_function()]. #' #' @param .f A function, formula, or atomic vector. #' #' If a __function__, it is used as is. #' #' If a __formula__, e.g. `~ .x + 2`, it is converted to a function. There #' are three ways to refer to the arguments: #' #' * For a single argument function, use `.` #' * For a two argument function, use `.x` and `.y` #' * For more arguments, use `..1`, `..2`, `..3` etc #' #' This syntax allows you to create very compact anonymous functions. #' #' If __character vector__, __numeric vector__, or __list__, it #' is converted to an extractor function. Character vectors index by name #' and numeric vectors index by position; use a list to index by position #' and name at different levels. Within a list, wrap strings in [get-attr()] #' to extract named attributes. If a component is not present, the value of #' `.default` will be returned. #' @param .default,.null Optional additional argument for extractor functions #' (i.e. when `.f` is character, integer, or list). Returned when #' value is absent (does not exist) or empty (has length 0). #' `.null` is deprecated; please use `.default` instead. #' @param ... Additional arguments passed on to methods. #' @export #' @examples #' as_mapper(~ . + 1) #' as_mapper(1) #' #' as_mapper(c("a", "b", "c")) #' # Equivalent to function(x) x[["a"]][["b"]][["c"]] #' #' as_mapper(list(1, "a", 2)) #' # Equivalent to function(x) x[[1]][["a"]][[2]] #' #' as_mapper(list(1, attr_getter("a"))) #' # Equivalent to function(x) attr(x[[1]], "a") #' #' as_mapper(c("a", "b", "c"), .null = NA) as_mapper <- function(.f, ...) { UseMethod("as_mapper") } #' @export #' @rdname as_mapper #' @usage NULL as_function <- function(...) { warning( "`as_function()` is deprecated; please use `as_mapper()` or `rlang::as_function()` instead", call. = FALSE ) as_mapper(...) } #' Pluck out a single an element from a vector or environment #' #' @description #' #' This is a generalised form of `[[` which allows you to index deeply #' and flexibly into data structures. It supports R standard accessors #' like integer positions and string names, and also accepts arbitrary #' accessor functions, i.e. functions that take an object and return #' some internal piece. #' #' `pluck()` is often more readable than a mix of operators and #' accessors because it reads linearly and is free of syntactic #' cruft. Compare: \code{accessor(x[[1]])$foo} to `pluck(x, 1, #' accessor, "foo")`. #' #' Furthermore, `pluck()` never partial-matches unlike `$` which will #' select the `disp` object if you write `mtcars$di`. #' #' @details #' #' Since it handles arbitrary accessor functions, `pluck()` is a type #' of composition operator. However, it is indexing-oriented thanks to #' its handling of strings and integers. By the same token is also #' explicit regarding the intent of the composition (e.g. extraction). #' #' @param .x A vector or environment #' @param ... A list of accessors for indexing into the object. Can be #' an integer position, a string name, or an accessor function. If #' the object being indexed is an S4 object, accessing it by name #' will return the corresponding slot. #' #' These dots [splice lists automatically][rlang::dots_splice]. This #' means you can supply arguments and lists of arguments #' indistinctly. #' @param .default Value to use if target is empty or absent. #' @keywords internal #' @export #' @examples #' # pluck() supports integer positions, string names, and functions. #' # Using functions, you can easily extend pluck(). Let's create a #' # list of data structures: #' obj1 <- list("a", list(1, elt = "foobar")) #' obj2 <- list("b", list(2, elt = "foobaz")) #' x <- list(obj1, obj2) #' #' # And now an accessor for these complex data structures: #' my_element <- function(x) x[[2]]$elt #' #' # The accessor can then be passed to pluck: #' pluck(x, 1, my_element) #' pluck(x, 2, my_element) #' #' # Even for this simple data structure, this is more readable than #' # the alternative form because it requires you to read both from #' # right-to-left and from left-to-right in different parts of the #' # expression: #' my_element(x[[1]]) #' #' #' # This technique is used for plucking into attributes with #' # attr_getter(). It takes an attribute name and returns a function #' # to access the attribute: #' obj1 <- structure("obj", obj_attr = "foo") #' obj2 <- structure("obj", obj_attr = "bar") #' x <- list(obj1, obj2) #' #' # pluck() is handy for extracting deeply into a data structure. #' # Here we'll first extract by position, then by attribute: #' pluck(x, 1, attr_getter("obj_attr")) # From first object #' pluck(x, 2, attr_getter("obj_attr")) # From second object #' #' #' # pluck() splices lists of arguments automatically. The following #' # pluck is equivalent to the one above: #' idx <- list(1, attr_getter("obj_attr")) #' pluck(x, idx) pluck <- function(.x, ..., .default = NULL) { .Call(extract_impl, .x, dots_splice(...), .default) } #' @export #' @rdname pluck #' @param attr An attribute name as string. attr_getter <- function(attr) { force(attr) function(x) attr(x, attr) } # Vectors ----------------------------------------------------------------- #' @export #' @rdname as_mapper as_mapper.character <- function(.f, ..., .null, .default = NULL) { .default <- find_extract_default(.null, .default) plucker(as.list(.f), .default) } #' @export #' @rdname as_mapper as_mapper.numeric <- function(.f, ..., .null, .default = NULL) { .default <- find_extract_default(.null, .default) plucker(as.list(.f), .default) } #' @export #' @rdname as_mapper as_mapper.list <- function(.f, ..., .null, .default = NULL) { .default <- find_extract_default(.null, .default) plucker(.f, .default) } find_extract_default <- function(.null, .default) { if (!missing(.null)) { # warning("`.null` is deprecated; please use `.default` instead", call. = FALSE) .null } else { .default } } plucker <- function(i, default) { # Interpolation creates a closure with a more readable source expr_interp(function(x, ...) pluck(x, !! i, .default = !! default) ) } # Default ----------------------------------------------------------------- #' @export as_mapper.default <- function(.f, ...) { rlang::as_closure(.f) } purrr/R/rerun.R0000644000176200001440000000213113125254534013104 0ustar liggesusers#' Re-run expressions multiple times. #' #' This is a convenient way of generating sample data. It works similarly to #' \code{\link{replicate}(..., simplify = FALSE)}. #' #' @param .n Number of times to run expressions #' @param ... Expressions to re-run. #' @return A list of length `.n`. Each element of `...` will be #' re-run once for each `.n`. It #' #' There is one special case: if there's a single unnamed input, the second #' level list will be dropped. In this case, `rerun(n, x)` behaves like #' `replicate(n, x, simplify = FALSE)`. #' @export #' @examples #' 10 %>% rerun(rnorm(5)) #' 10 %>% #' rerun(x = rnorm(5), y = rnorm(5)) %>% #' map_dbl(~ cor(.x$x, .x$y)) rerun <- function(.n, ...) { dots <- quos(...) # Special case: if single unnamed argument, insert directly into the output # rather than wrapping in a list. if (length(dots) == 1 && !has_names(dots)) { dots <- dots[[1]] eval_dots <- eval_tidy } else { eval_dots <- function(x) lapply(x, eval_tidy) } out <- vector("list", .n) for (i in seq_len(.n)) { out[[i]] <- eval_dots(dots) } out } purrr/R/utils.R0000644000176200001440000000431313147024312013106 0ustar liggesusers#' Pipe operator #' #' @name %>% #' @rdname pipe #' @keywords internal #' @export #' @importFrom magrittr %>% #' @usage lhs \%>\% rhs NULL maybe_as_data_frame <- function(out, x) { if (is.data.frame(x)) { tibble::as_tibble(out) } else { out } } recycle_args <- function(args) { lengths <- map_int(args, length) n <- max(lengths) stopifnot(all(lengths == 1L | lengths == n)) to_recycle <- lengths == 1L args[to_recycle] <- lapply(args[to_recycle], function(x) rep.int(x, n)) args } names2 <- function(x) { names(x) %||% rep("", length(x)) } #' Default value for `NULL`. #' #' This infix function makes it easy to replace `NULL`s with a #' default value. It's inspired by the way that Ruby's or operation (`||`) #' works. #' #' @param x,y If `x` is NULL, will return `y`; otherwise returns #' `x`. #' @export #' @name null-default #' @examples #' 1 %||% 2 #' NULL %||% 2 `%||%` <- function(x, y) { if (is.null(x)) { y } else { x } } #' Infix attribute accessor #' #' @param x Object #' @param name Attribute name #' @export #' @name get-attr #' @examples #' factor(1:3) %@% "levels" #' mtcars %@% "class" `%@%` <- function(x, name) attr(x, name, exact = TRUE) #' Generate random sample from a Bernoulli distribution #' #' @param n Number of samples #' @param p Probability of getting `TRUE` #' @return A logical vector #' @export #' @examples #' rbernoulli(10) #' rbernoulli(100, 0.1) rbernoulli <- function(n, p = 0.5) { stats::runif(n) > (1 - p) } #' Generate random sample from a discrete uniform distribution #' #' @param n Number of samples to draw. #' @param a,b Range of the distribution (inclusive). #' @export #' @examples #' table(rdunif(1e3, 10)) #' table(rdunif(1e3, 10, -5)) rdunif <- function(n, b, a = 1) { stopifnot(is.numeric(a), length(a) == 1) stopifnot(is.numeric(b), length(b) == 1) a1 <- min(a, b) b1 <- max(a, b) sample(b1 - a1 + 1, n, replace = TRUE) + a1 - 1 } # magrittr placeholder globalVariables(".") has_names <- function(x) { nms <- names(x) if (is.null(nms)) { rep_along(x, FALSE) } else { !(is.na(nms) | nms == "") } } ndots <- function(...) nargs() is_names <- function(nms) { is_character(nms) && !any(is.na(nms) | nms == "") } purrr/R/splice.R0000644000176200001440000000152313124217327013232 0ustar liggesusers#' Splice objects and lists of objects into a list #' #' This splices all arguments into a list. Non-list objects and lists #' with a S3 class are encapsulated in a list before concatenation. #' #' @param ... Objects to concatenate. #' @return A list. #' @export #' @examples #' inputs <- list(arg1 = "a", arg2 = "b") #' #' # splice() concatenates the elements of inputs with arg3 #' splice(inputs, arg3 = c("c1", "c2")) %>% str() #' list(inputs, arg3 = c("c1", "c2")) %>% str() #' c(inputs, arg3 = c("c1", "c2")) %>% str() splice <- function(...) { splice_if(list(...), is_bare_list) } splice_if <- function(.x, .p) { unspliced <- !probe(.x, .p) out <- modify_if(.x, unspliced, list) # Copy outer names to inner if (!is.null(names(.x))) { out[unspliced] <- map2(out[unspliced], names(out)[unspliced], set_names) } flatten(out) } purrr/R/output.R0000644000176200001440000001061713137651733013326 0ustar liggesusers#' Capture side effects. #' #' These functions wrap functions so that instead of generating side effects #' through printed output, messages, warnings, and errors, they return enhanced #' output. They are all adverbs because they modify the action of a verb (a #' function). #' #' @inheritParams map #' @param quiet Hide errors (`TRUE`, the default), or display them #' as they occur? #' @param otherwise Default value to use when an error occurs. #' #' @return `safely`: wrapped function instead returns a list with #' components `result` and `error`. One value is always `NULL`. #' #' `quietly`: wrapped function instead returns a list with components #' `result`, `output`, `messages` and `warnings`. #' #' `possibly`: wrapped function uses a default value (`otherwise`) #' whenever an error occurs. #' #' @export #' @examples #' safe_log <- safely(log) #' safe_log(10) #' safe_log("a") #' #' list("a", 10, 100) %>% #' map(safe_log) %>% #' transpose() #' #' # This is a bit easier to work with if you supply a default value #' # of the same type and use the simplify argument to transpose(): #' safe_log <- safely(log, otherwise = NA_real_) #' list("a", 10, 100) %>% #' map(safe_log) %>% #' transpose() %>% #' simplify_all() #' #' # To replace errors with a default value, use possibly(). #' list("a", 10, 100) %>% #' map_dbl(possibly(log, NA_real_)) #' #' # For interactive usage, auto_browse() is useful because it automatically #' # starts a browser() in the right place. #' f <- function(x) { #' y <- 20 #' if (x > 5) { #' stop("!") #' } else { #' x #' } #' } #' if (interactive()) { #' map(1:6, auto_browse(f)) #' } #' #' # It doesn't make sense to use auto_browse with primitive functions, #' # because they are implemented in C so there's no useful environment #' # for you to interact with. safely <- function(.f, otherwise = NULL, quiet = TRUE) { .f <- as_mapper(.f) function(...) capture_error(.f(...), otherwise, quiet) } #' @export #' @rdname safely quietly <- function(.f) { .f <- as_mapper(.f) function(...) capture_output(.f(...)) } #' @export #' @rdname safely possibly <- function(.f, otherwise, quiet = TRUE) { .f <- as_mapper(.f) force(otherwise) function(...) { tryCatch(.f(...), error = function(e) { if (!quiet) message("Error: ", e$message) otherwise }, interrupt = function(e) { stop("Terminated by user", call. = FALSE) } ) } } #' @export #' @rdname safely auto_browse <- function(.f) { if (is_primitive(.f)) { abort("Can not auto_browse() primitive functions") } function(...) { withCallingHandlers( .f(...), error = function(e) { # 1: h(simpleError(msg, call)) # 2: .handleSimpleError(function (e) <...> # 3: stop(...) frame <- ctxt_frame(4) browse_in_frame(frame) }, warning = function(e) { if (getOption("warn") >= 2) { frame <- ctxt_frame(7) browse_in_frame(frame) } }, interrupt = function(e) { stop("Terminated by user", call. = FALSE) } ) } } browse_in_frame <- function(frame) { # ESS should problably set `.Platform$GUI == "ESS"` # In the meantime, check that ESSR is attached if (is_scoped("ESSR")) { # Workaround ESS issue with_env(frame$env, on.exit({ browser() NULL })) return_from(frame) } else { eval_bare(quote(browser()), env = frame$env) } } capture_error <- function(code, otherwise = NULL, quiet = TRUE) { tryCatch( list(result = code, error = NULL), error = function(e) { if (!quiet) message("Error: ", e$message) list(result = otherwise, error = e) }, interrupt = function(e) { stop("Terminated by user", call. = FALSE) } ) } capture_output <- function(code) { warnings <- character() wHandler <- function(w) { warnings <<- c(warnings, w$message) invokeRestart("muffleWarning") } messages <- character() mHandler <- function(m) { messages <<- c(messages, m$message) invokeRestart("muffleMessage") } temp <- file() sink(temp) on.exit({ sink() close(temp) }) result <- withCallingHandlers( code, warning = wHandler, message = mHandler ) output <- paste0(readLines(temp, warn = FALSE), collapse = "\n") list( result = result, output = output, warnings = warnings, messages = messages ) } purrr/R/reduce.R0000644000176200001440000001170313147024610013217 0ustar liggesusers#' Reduce a list to a single value by iteratively applying a binary function. #' #' `reduce()` combines from the left, `reduce_right()` combines from #' the right. `reduce(list(x1, x2, x3), f)` is equivalent to #' `f(f(x1, x2), x3)`; `reduce_right(list(x1, x2, x3), f)` is equivalent to #' `f(f(x3, x2), x1)`. #' #' @inheritParams map #' @param .y For `reduce2()`, an additional argument that is passed to #' `.f`. If `init` is not set, `.y` should be 1 element shorter than #' `.x`. #' @param .f For `reduce()`, a 2-argument function. The function will be #' passed the accumulated value as the first argument and the "next" value #' as the second argument. #' #' For `reduce2()`, a 3-argument function. The function will be passed the #' accumulated value as the first argument, the next value of `.x` as the #' second argument, and the next value of `.y` as the third argument. #' #' @param .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 `reduce` returns a correct value when `.x` #' is empty. If missing, and `x` is empty, will throw an error. #' @export #' @examples #' 1:3 %>% reduce(`+`) #' 1:10 %>% reduce(`*`) #' #' paste2 <- function(x, y, sep = ".") paste(x, y, sep = sep) #' letters[1:4] %>% reduce(paste2) #' letters[1:4] %>% reduce2(c("-", ".", "-"), paste2) #' #' samples <- rerun(2, sample(10, 5)) #' samples #' reduce(samples, union) #' reduce(samples, intersect) #' #' x <- list(c(0, 1), c(2, 3), c(4, 5)) #' x %>% reduce(c) #' x %>% reduce_right(c) #' # Equivalent to: #' x %>% rev() %>% reduce(c) reduce <- function(.x, .f, ..., .init) { reduce_impl(.x, .f, ..., .init = .init, .left = TRUE) } #' @export #' @rdname reduce reduce_right <- function(.x, .f, ..., .init) { reduce_impl(.x, .f, ..., .init = .init, .left = FALSE) } #' @export #' @rdname reduce reduce2 <- function(.x, .y, .f, ..., .init) { reduce2_impl(.x, .y, .f, ..., .init = .init, .left = TRUE) } #' @export #' @rdname reduce reduce2_right <- function(.x, .y, .f, ..., .init) { reduce2_impl(.x, .f, .y, ..., .init = .init, .left = FALSE) } reduce2_impl <- function(.x, .y, .f, ..., .init, .left = TRUE) { out <- reduce_init(.x, .init, left = .left) x_idx <- reduce_index(.x, .init, left = .left) y_idx <- reduce_index(.y, NULL, left = .left) if (length(x_idx) != length(y_idx)) { stop("`.y` does not have length ", length(x_idx)) } .f <- as_mapper(.f, ...) for (i in seq_along(x_idx)) { x_i <- x_idx[[i]] y_i <- y_idx[[i]] out <- .f(out, .x[[x_i]], .y[[y_i]], ...) } out } reduce_impl <- function(.x, .f, ..., .init, .left = TRUE) { out <- reduce_init(.x, .init, left = .left) idx <- reduce_index(.x, .init, left = .left) .f <- as_mapper(.f, ...) for (i in idx) { out <- .f(out, .x[[i]], ...) } out } reduce_init <- function(x, init, left = TRUE) { if (!missing(init)) { init } else { if (is_empty(x)) { stop("`.x` is empty, and no `.init` supplied", call. = FALSE) } else if (left) { x[[1]] } else { x[[length(x)]] } } } reduce_index <- function(x, init, left = TRUE) { n <- length(x) if (!missing(init)) { if (left) { seq_len(n) } else { rev(seq_len(n)) } } else { if (left) { seq_len2(2L, n) } else { rev(seq_len2(1L, n - 1L)) } } } seq_len2 <- function(start, end) { if (start > end) { return(integer(0)) } start:end } #' Accumulate recursive folds across a list #' #' `accumulate` applies a function recursively over a list from the left, while #' `accumulate_right` applies the function from the right. Unlike `reduce` #' both functions keep the intermediate results. #' #' @inheritParams reduce #' @export #' @examples #' 1:3 %>% accumulate(`+`) #' 1:10 %>% accumulate_right(`*`) #' #' # From Haskell's scanl documentation #' 1:10 %>% accumulate(max, .init = 5) #' #' # Understanding the arguments .x and .y when .f #' # is a lambda function #' # .x is the accumulating value #' 1:10 %>% accumulate(~ .x) #' # .y is element in the list #' 1:10 %>% accumulate(~ .y) #' #' # Simulating stochastic processes with drift #' \dontrun{ #' library(dplyr) #' library(ggplot2) #' #' rerun(5, rnorm(100)) %>% #' set_names(paste0("sim", 1:5)) %>% #' map(~ accumulate(., ~ .05 + .x + .y)) %>% #' map_dfr(~ data_frame(value = .x, step = 1:100), .id = "simulation") %>% #' ggplot(aes(x = step, y = value)) + #' geom_line(aes(color = simulation)) + #' ggtitle("Simulations of a random walk with drift") #' } accumulate <- function(.x, .f, ..., .init) { .f <- as_mapper(.f, ...) f <- function(x, y) { .f(x, y, ...) } Reduce(f, .x, init = .init, accumulate = TRUE) } #' @export #' @rdname accumulate accumulate_right <- function(.x, .f, ..., .init) { .f <- as_mapper(.f, ...) # Note the order of arguments is switched f <- function(x, y) { .f(y, x, ...) } Reduce(f, .x, init = .init, right = TRUE, accumulate = TRUE) } purrr/R/find-position.R0000644000176200001440000000356113137651733014550 0ustar liggesusers#' Find the value or position of the first match. #' #' @inheritParams every #' @inheritParams map #' @param .right If `FALSE`, the default, starts at the beginning #' of the vector and move towards the end; if `TRUE`, starts at the end #' of the vector and moves towards the beginning. #' @return `detect` the value of the first item that matches the #' predicate; `detect_index` the position of the matching item. #' If not found, `detect` returns `NULL` and `detect_index` #' returns 0. #' @export #' @examples #' is_even <- function(x) x %% 2 == 0 #' #' 3:10 %>% detect(is_even) #' 3:10 %>% detect_index(is_even) #' #' 3:10 %>% detect(is_even, .right = TRUE) #' 3:10 %>% detect_index(is_even, .right = TRUE) #' #' #' # Since `.f` is passed to as_mapper(), you can supply a #' # lambda-formula or a pluck object: #' x <- list( #' list(1, foo = FALSE), #' list(2, foo = TRUE), #' list(3, foo = TRUE) #' ) #' #' detect(x, "foo") #' detect_index(x, "foo") detect <- function(.x, .f, ..., .right = FALSE, .p) { if (!missing(.p)) { warn("`.p` has been renamed to `.f`", "purrr_2.2.3") .f <- .p } .f <- as_mapper(.f, ...) for (i in index(.x, .right)) { if (is_true(.f(.x[[i]], ...))) return(.x[[i]]) } NULL } #' @export #' @rdname detect detect_index <- function(.x, .f, ..., .right = FALSE, .p) { if (!missing(.p)) { warn("`.p` has been renamed to `.f`", "purrr_2.2.3") .f <- .p } .f <- as_mapper(.f, ...) for (i in index(.x, .right)) { if (is_true(.f(.x[[i]], ...))) return(i) } 0L } index <- function(x, right = FALSE) { idx <- seq_along(x) if (right) idx <- rev(idx) idx } #' Does a list contain an object? #' #' @inheritParams map #' @param .y Object to test for #' @export #' @examples #' x <- list(1:10, 5, 9.9) #' x %>% has_element(1:10) #' x %>% has_element(3) has_element <- function(.x, .y) { some(.x, identical, .y) } purrr/R/head-tail.R0000644000176200001440000000131413102346452013577 0ustar liggesusers#' Find head/tail that all satisfies a predicate. #' #' @inheritParams map_if #' @inheritParams map #' @return A vector the same type as `.x`. #' @export #' @examples #' pos <- function(x) x >= 0 #' head_while(5:-5, pos) #' tail_while(5:-5, negate(pos)) #' #' big <- function(x) x > 100 #' head_while(0:10, big) #' tail_while(0:10, big) head_while <- function(.x, .p, ...) { # Find location of first FALSE loc <- detect_index(.x, negate(.p), ...) if (loc == 0) return(.x) .x[seq_len(loc - 1)] } #' @export #' @rdname head_while tail_while <- function(.x, .p, ...) { # Find location of last FALSE loc <- detect_index(.x, negate(.p), ..., .right = TRUE) if (loc == 0) return(.x) .x[-seq_len(loc)] } purrr/R/invoke.R0000644000176200001440000001066013124217327013250 0ustar liggesusers#' Invoke functions. #' #' This pair of functions make it easier to combine a function and list #' of parameters to get a result. `invoke` is a wrapper around #' `do.call` that makes it easy to use in a pipe. `invoke_map` #' makes it easier to call lists of functions with lists of parameters. #' #' @param .f For `invoke`, a function; for `invoke_map` a #' list of functions. #' @param .x For `invoke`, an argument-list; for `invoke_map` a #' list of argument-lists the same length as `.f` (or length 1). #' The default argument, `list(NULL)`, will be recycled to the #' same length as `.f`, and will call each function with no #' arguments (apart from any supplied in `...`. #' @param ... Additional arguments passed to each function. #' @param .env Environment in which [do.call()] should #' evaluate a constructed expression. This only matters if you pass #' as `.f` the name of a function rather than its value, or as #' `.x` symbols of objects rather than their values. #' @inheritParams map #' @export #' @family map variants #' @examples #' # Invoke a function with a list of arguments #' invoke(runif, list(n = 10)) #' # Invoke a function with named arguments #' invoke(runif, n = 10) #' #' # Combine the two: #' invoke(paste, list("01a", "01b"), sep = "-") #' # That's more natural as part of a pipeline: #' list("01a", "01b") %>% #' invoke(paste, ., sep = "-") #' #' # Invoke a list of functions, each with different arguments #' invoke_map(list(runif, rnorm), list(list(n = 10), list(n = 5))) #' # Or with the same inputs: #' invoke_map(list(runif, rnorm), list(list(n = 5))) #' invoke_map(list(runif, rnorm), n = 5) #' # Or the same function with different inputs: #' invoke_map("runif", list(list(n = 5), list(n = 10))) #' #' # Or as a pipeline #' list(m1 = mean, m2 = median) %>% invoke_map(x = rcauchy(100)) #' list(m1 = mean, m2 = median) %>% invoke_map_dbl(x = rcauchy(100)) #' #' # Note that you can also match by position by explicitly omitting `.x`. #' # This can be useful when the argument names of the functions are not #' # identical #' list(m1 = mean, m2 = median) %>% #' invoke_map(, rcauchy(100)) #' #' # If you have pairs of function name and arguments, it's natural #' # to store them in a data frame. Here we use a tibble because #' # it has better support for list-columns #' df <- tibble::tibble( #' f = c("runif", "rpois", "rnorm"), #' params = list( #' list(n = 10), #' list(n = 5, lambda = 10), #' list(n = 10, mean = -3, sd = 10) #' ) #' ) #' df #' invoke_map(df$f, df$params) invoke <- function(.f, .x = NULL, ..., .env = NULL) { .env <- .env %||% parent.frame() args <- c(as.list(.x), list(...)) do.call(.f, args, envir = .env) } as_invoke_function <- function(f) { if (is.function(f)) { list(f) } else { f } } #' @rdname invoke #' @export invoke_map <- function(.f, .x = list(NULL), ..., .env = NULL) { .env <- .env %||% parent.frame() .f <- as_invoke_function(.f) map2(.f, .x, invoke, ..., .env = .env) } #' @rdname invoke #' @export invoke_map_lgl <- function(.f, .x = list(NULL), ..., .env = NULL) { .env <- .env %||% parent.frame() .f <- as_invoke_function(.f) map2_lgl(.f, .x, invoke, ..., .env = .env) } #' @rdname invoke #' @export invoke_map_int <- function(.f, .x = list(NULL), ..., .env = NULL) { .env <- .env %||% parent.frame() .f <- as_invoke_function(.f) map2_int(.f, .x, invoke, ..., .env = .env) } #' @rdname invoke #' @export invoke_map_dbl <- function(.f, .x = list(NULL), ..., .env = NULL) { .env <- .env %||% parent.frame() .f <- as_invoke_function(.f) map2_dbl(.f, .x, invoke, ..., .env = .env) } #' @rdname invoke #' @export invoke_map_chr <- function(.f, .x = list(NULL), ..., .env = NULL) { .env <- .env %||% parent.frame() .f <- as_invoke_function(.f) map2_chr(.f, .x, invoke, ..., .env = .env) } #' @rdname invoke #' @export invoke_map_dfr <- function(.f, .x = list(NULL), ..., .env = NULL) { .env <- .env %||% parent.frame() .f <- as_invoke_function(.f) map2_dfr(.f, .x, invoke, ..., .env = .env) } #' @rdname invoke #' @export invoke_map_dfc <- function(.f, .x = list(NULL), ..., .env = NULL) { .env <- .env %||% parent.frame() .f <- as_invoke_function(.f) map2_dfc(.f, .x, invoke, ..., .env = .env) } #' @rdname invoke #' @export #' @usage NULL invoke_map_df <- invoke_map_dfr #' @rdname invoke #' @export #' @usage NULL map_call <- function(.x, .f, ...) { warning("`map_call()` is deprecated. Please use `invoke()` instead.") invoke(.f, .x, ...) } purrr/R/map2-pmap.R0000644000176200001440000001241313137651733013554 0ustar liggesusers#' Map over multiple inputs simultaneously. #' #' These functions are variants of `map()` iterate over multiple #' arguments in parallel. `map2()` and `walk2()` are specialised for the two #' argument case; `pmap()` and `pwalk()` allow you to provide any number of #' arguments in a list. #' #' Note that arguments to be vectorised over come before the `.f`, #' and arguments that are supplied to every call come after `.f`. #' #' @inheritParams map #' @param .x,.y Vectors of the same length. A vector of length 1 will #' be recycled. #' @param .l A list of lists. The length of `.l` determines the #' number of arguments that `.f` will be called with. List #' names will be used if present. #' @return An atomic vector, list, or data frame, depending on the suffix. #' Atomic vectors and lists will be named if `.x` or the first #' element of `.l` is named. #' #' If all input is length 0, the output will be length 0. If any #' input is length 1, it will be recycled to the length of the longest. #' @export #' @family map variants #' @examples #' x <- list(1, 10, 100) #' y <- list(1, 2, 3) #' z <- list(5, 50, 500) #' #' map2(x, y, ~ .x + .y) #' # Or just #' map2(x, y, `+`) #' #' # Split into pieces, fit model to each piece, then predict #' by_cyl <- mtcars %>% split(.$cyl) #' mods <- by_cyl %>% map(~ lm(mpg ~ wt, data = .)) #' map2(mods, by_cyl, predict) #' #' pmap(list(x, y, z), sum) #' #' # Matching arguments by position #' pmap(list(x, y, z), function(a, b ,c) a / (b + c)) #' #' # Matching arguments by name #' l <- list(a = x, b = y, c = z) #' pmap(l, function(c, b, a) a / (b + c)) #' #' # Vectorizing a function over multiple arguments #' df <- data.frame( #' x = c("apple", "banana", "cherry"), #' pattern = c("p", "n", "h"), #' replacement = c("x", "f", "q"), #' stringsAsFactors = FALSE #' ) #' pmap(df, gsub) #' pmap_chr(df, gsub) #' #' ## Use `...` to absorb unused components of input list .l #' df <- data.frame( #' x = 1:3 + 0.1, #' y = 3:1 - 0.1, #' z = letters[1:3] #' ) #' plus <- function(x, y) x + y #' \dontrun{ #' ## this won't work #' pmap(df, plus) #' } #' ## but this will #' plus2 <- function(x, y, ...) x + y #' pmap_dbl(df, plus2) #' map2 <- function(.x, .y, .f, ...) { .f <- as_mapper(.f, ...) .Call(map2_impl, environment(), ".x", ".y", ".f", "list") } #' @export #' @rdname map2 map2_lgl <- function(.x, .y, .f, ...) { .f <- as_mapper(.f, ...) .Call(map2_impl, environment(), ".x", ".y", ".f", "logical") } #' @export #' @rdname map2 map2_int <- function(.x, .y, .f, ...) { .f <- as_mapper(.f, ...) .Call(map2_impl, environment(), ".x", ".y", ".f", "integer") } #' @export #' @rdname map2 map2_dbl <- function(.x, .y, .f, ...) { .f <- as_mapper(.f, ...) .Call(map2_impl, environment(), ".x", ".y", ".f", "double") } #' @export #' @rdname map2 map2_chr <- function(.x, .y, .f, ...) { .f <- as_mapper(.f, ...) .Call(map2_impl, environment(), ".x", ".y", ".f", "character") } #' @rdname map2 #' @export map2_dfr <- function(.x, .y, .f, ..., .id = NULL) { if (!is_installed("dplyr")) { abort("`map2_dfr()` requires dplyr") } .f <- as_mapper(.f, ...) res <- map2(.x, .y, .f, ...) dplyr::bind_rows(res, .id = .id) } #' @rdname map2 #' @export map2_dfc <- function(.x, .y, .f, ...) { if (!is_installed("dplyr")) { abort("`map2_dfc()` requires dplyr") } .f <- as_mapper(.f, ...) res <- map2(.x, .y, .f, ...) dplyr::bind_cols(res) } #' @rdname map2 #' @export #' @usage NULL map2_df <- map2_dfr #' @export #' @rdname map2 walk2 <- function(.x, .y, .f, ...) { pwalk(list(.x, .y), .f, ...) invisible(.x) } #' @export #' @rdname map2 pmap <- function(.l, .f, ...) { .f <- as_mapper(.f, ...) if (is.data.frame(.l)) { .l <- as.list(.l) } .Call(pmap_impl, environment(), ".l", ".f", "list") } #' @export #' @rdname map2 pmap_lgl <- function(.l, .f, ...) { .f <- as_mapper(.f, ...) if (is.data.frame(.l)) { .l <- as.list(.l) } .Call(pmap_impl, environment(), ".l", ".f", "logical") } #' @export #' @rdname map2 pmap_int <- function(.l, .f, ...) { .f <- as_mapper(.f, ...) if (is.data.frame(.l)) { .l <- as.list(.l) } .Call(pmap_impl, environment(), ".l", ".f", "integer") } #' @export #' @rdname map2 pmap_dbl <- function(.l, .f, ...) { .f <- as_mapper(.f, ...) if (is.data.frame(.l)) { .l <- as.list(.l) } .Call(pmap_impl, environment(), ".l", ".f", "double") } #' @export #' @rdname map2 pmap_chr <- function(.l, .f, ...) { .f <- as_mapper(.f, ...) if (is.data.frame(.l)) { .l <- as.list(.l) } .Call(pmap_impl, environment(), ".l", ".f", "character") } #' @rdname map2 #' @export pmap_dfr <- function(.l, .f, ..., .id = NULL) { if (!is_installed("dplyr")) { abort("`pmap_dfr()` requires dplyr") } .f <- as_mapper(.f, ...) res <- pmap(.l, .f, ...) dplyr::bind_rows(res, .id = .id) } #' @rdname map2 #' @export pmap_dfc <- function(.l, .f, ...) { if (!is_installed("dplyr")) { abort("`pmap_dfc()` requires dplyr") } .f <- as_mapper(.f, ...) res <- pmap(.l, .f, ...) dplyr::bind_cols(res) } #' @rdname map2 #' @export #' @usage NULL pmap_df <- pmap_dfr #' @export #' @rdname map2 pwalk <- function(.l, .f, ...) { .f <- as_mapper(.f, ...) args_list <- recycle_args(.l) %>% transpose() for (args in args_list) { do.call(".f", c(args, list(...))) } invisible(.l) } purrr/R/depth.R0000644000176200001440000000103513137651733013064 0ustar liggesusers#' Compute the depth of a vector #' #' The depth of a vector is basically how many levels that you can index #' into it. #' #' @param x A vector #' @return An integer. #' @export #' @examples #' x <- list( #' list(), #' list(list()), #' list(list(list(1))) #' ) #' vec_depth(x) #' x %>% map_int(vec_depth) vec_depth <- function(x) { if (is_null(x)) { 0L } else if (is_atomic(x)) { 1L } else if (is_list(x)) { depths <- map_int(x, vec_depth) 1L + max(depths, 0L) } else { abort("`x` must be a vector") } } purrr/R/arrays.R0000644000176200001440000000463313102346452013257 0ustar liggesusers#' Coerce array to list #' #' `array_branch()` and `array_tree()` enable arrays to be #' used with purrr's functionals by turning them into lists. The #' details of the coercion are controlled by the `margin` #' argument. `array_tree()` creates an hierarchical list (a tree) #' that has as many levels as dimensions specified in `margin`, #' while `array_branch()` creates a flat list (by analogy, a #' branch) along all mentioned dimensions. #' #' When no margin is specified, all dimensions are used by #' default. When `margin` is a numeric vector of length zero, the #' whole array is wrapped in a list. #' @param array An array to coerce into a list. #' @param margin A numeric vector indicating the positions of the #' indices to be to be enlisted. If `NULL`, a full margin is #' used. If `numeric(0)`, the array as a whole is wrapped in a #' list. #' @name array-coercion #' @export #' @examples #' # We create an array with 3 dimensions #' x <- array(1:12, c(2, 2, 3)) #' #' # A full margin for such an array would be the vector 1:3. This is #' # the default if you don't specify a margin #' #' # Creating a branch along the full margin is equivalent to #' # as.list(array) and produces a list of size length(x): #' array_branch(x) %>% str() #' #' # A branch along the first dimension yields a list of length 2 #' # with each element containing a 2x3 array: #' array_branch(x, 1) %>% str() #' #' # A branch along the first and third dimensions yields a list of #' # length 2x3 whose elements contain a vector of length 2: #' array_branch(x, c(1, 3)) %>% str() #' #' # Creating a tree from the full margin creates a list of lists of #' # lists: #' array_tree(x) %>% str() #' #' # The ordering and the depth of the tree are controlled by the #' # margin argument: #' array_tree(x, c(3, 1)) %>% str() array_branch <- function(array, margin = NULL) { dim(array) <- dim(array) %||% length(array) margin <- margin %||% seq_along(dim(array)) if (length(margin) == 0) { list(array) } else { apply(array, margin, list) %>% flatten() } } #' @rdname array-coercion #' @export array_tree <- function(array, margin = NULL) { dim(array) <- dim(array) %||% length(array) margin <- margin %||% seq_along(dim(array)) if (length(margin) > 1) { new_margin <- ifelse(margin[-1] > margin[[1]], margin[-1] - 1, margin[-1]) apply(array, margin[[1]], . %>% array_tree(new_margin)) } else { array_branch(array, margin) } } purrr/R/imap.R0000644000176200001440000000333213102346452012677 0ustar liggesusers#' Apply a function to each element of a vector, and its index #' #' `imap_xxx(x, ...)`, an indexed map, is short hand for #' `map2(x, names(x), ...)` if `x` has names, or `map2(x, seq_along(x), ...)` #' if it does not. This is useful if you need to compute on both the value #' and the position of an element. #' #' @inheritParams map #' @return A vector the same length as `.x`. #' @export #' @family map variants #' @examples #' # Note that when using the formula shortcut, the first argument #' # is the value, and the second is the position #' imap_chr(sample(10), ~ paste0(.y, ": ", .x)) #' iwalk(mtcars, ~ cat(.y, ": ", median(.x), "\n", sep = "")) imap <- function(.x, .f, ...) { .f <- as_mapper(.f, ...) map2(.x, vec_index(.x), .f, ...) } #' @rdname imap #' @export imap_lgl <- function(.x, .f, ...) { .f <- as_mapper(.f, ...) map2_lgl(.x, vec_index(.x), .f, ...) } #' @rdname imap #' @export imap_chr <- function(.x, .f, ...) { .f <- as_mapper(.f, ...) map2_chr(.x, vec_index(.x), .f, ...) } #' @rdname imap #' @export imap_int <- function(.x, .f, ...) { .f <- as_mapper(.f, ...) map2_int(.x, vec_index(.x), .f, ...) } #' @rdname imap #' @export imap_dbl <- function(.x, .f, ...) { .f <- as_mapper(.f, ...) map2_dbl(.x, vec_index(.x), .f, ...) } #' @rdname imap #' @export imap_dfr <- function(.x, .f, ..., .id = NULL) { .f <- as_mapper(.f, ...) map2_dfr(.x, vec_index(.x), .f, ...) } #' @rdname imap #' @export imap_dfc <- function(.x, .f, ..., .id = NULL) { .f <- as_mapper(.f, ...) map2_dfc(.x, vec_index(.x), .f, ...) } #' @export #' @rdname imap iwalk <- function(.x, .f, ...) { .f <- as_mapper(.f, ...) walk2(.x, vec_index(.x), .f, ...) } vec_index <- function(x) { names(x) %||% seq_along(x) } purrr/R/flatten.R0000644000176200001440000000354113124220045013401 0ustar liggesusers#' Flatten a list of lists into a simple vector. #' #' These functions remove a level hierarchy from a list. They are similar to #' [unlist()], only ever remove a single layer of hierarchy, and #' are type-stable so you always know what the type of the output is. #' #' @param .x A list of flatten. The contents of the list can be anything for #' `flatten` (as a list is returned), but the contents must match the #' type for the other functions. #' @return `flatten()` returns a list, `flatten_lgl()` a logical #' vector, `flatten_int()` an integer vector, `flatten_dbl()` a #' double vector, and `flatten_chr()` a character vector. #' #' `flatten_dfr()` and `flatten_dfc()` return data frames created by #' row-binding and column-binding respectively. They require dplyr to #' be installed. #' @inheritParams map #' @export #' @examples #' x <- rerun(2, sample(4)) #' x #' x %>% flatten() #' x %>% flatten_int() #' #' # You can use flatten in conjunction with map #' x %>% map(1L) %>% flatten_int() #' # But it's more efficient to use the typed map instead. #' x %>% map_int(1L) flatten <- function(.x) { .Call(flatten_impl, .x) } #' @export #' @rdname flatten flatten_lgl <- function(.x) { .Call(vflatten_impl, .x, "logical") } #' @export #' @rdname flatten flatten_int <- function(.x) { .Call(vflatten_impl, .x, "integer") } #' @export #' @rdname flatten flatten_dbl <- function(.x) { .Call(vflatten_impl, .x, "double") } #' @export #' @rdname flatten flatten_chr <- function(.x) { .Call(vflatten_impl, .x, "character") } #' @export #' @rdname flatten flatten_dfr <- function(.x, .id = NULL) { res <- .Call(flatten_impl, .x) dplyr::bind_rows(res, .id = .id) } #' @export #' @rdname flatten flatten_dfc <- function(.x) { res <- .Call(flatten_impl, .x) dplyr::bind_cols(res) } #' @export #' @rdname flatten #' @usage NULL flatten_df <- flatten_dfr purrr/R/cross.R0000644000176200001440000001252413102346452013105 0ustar liggesusers#' Produce all combinations of list elements #' #' `cross2()` returns the product set of the elements of #' `.x` and `.y`. `cross3()` takes an additional #' `.z` argument. `cross()` takes a list `.l` and #' returns the cartesian product of all its elements in a list, with #' one combination by element. `cross_df()` is like #' `cross()` but returns a data frame, with one combination by #' row. #' #' `cross()`, `cross2()` and `cross3()` return the #' cartesian product is returned in wide format. This makes it more #' amenable to mapping operations. `cross_df()` returns the output #' in long format just as `expand.grid()` does. This is adapted #' to rowwise operations. #' #' When the number of combinations is large and the individual #' elements are heavy memory-wise, it is often useful to filter #' unwanted combinations on the fly with `.filter`. It must be #' a predicate function that takes the same number of arguments as the #' number of crossed objects (2 for `cross2()`, 3 for #' `cross3()`, `length(.l)` for `cross()`) and #' returns `TRUE` or `FALSE`. The combinations where the #' predicate function returns `TRUE` will be removed from the #' result. #' @seealso [expand.grid()] #' @param .x,.y,.z Lists or atomic vectors. #' @param .l A list of lists or atomic vectors. Alternatively, a data #' frame. `cross_df()` requires all elements to be named. #' @param .filter A predicate function that takes the same number of #' arguments as the number of variables to be combined. #' @return `cross2()`, `cross3()` and `cross()` #' always return a list. `cross_df()` always returns a data #' frame. `cross()` returns a list where each element is one #' combination so that the list can be directly mapped #' over. `cross_df()` returns a data frame where each row is one #' combination. #' @export #' @examples #' # We build all combinations of names, greetings and separators from our #' # list of data and pass each one to paste() #' data <- list( #' id = c("John", "Jane"), #' greeting = c("Hello.", "Bonjour."), #' sep = c("! ", "... ") #' ) #' #' data %>% #' cross() %>% #' map(lift(paste)) #' #' # cross() returns the combinations in long format: many elements, #' # each representing one combination. With cross_df() we'll get a #' # data frame in long format: crossing three objects produces a data #' # frame of three columns with each row being a particular #' # combination. This is the same format that expand.grid() returns. #' args <- data %>% cross_df() #' #' # In case you need a list in long format (and not a data frame) #' # just run as.list() after cross_df() #' args %>% as.list() #' #' # This format is often less pratical for functional programming #' # because applying a function to the combinations requires a loop #' out <- vector("list", length = nrow(args)) #' for (i in seq_along(out)) #' out[[i]] <- map(args, i) %>% invoke(paste, .) #' out #' #' # It's easier to transpose and then use invoke_map() #' args %>% transpose() %>% map_chr(~ invoke(paste, .)) #' #' # Unwanted combinations can be filtered out with a predicate function #' filter <- function(x, y) x >= y #' cross2(1:5, 1:5, .filter = filter) %>% str() #' #' # To give names to the components of the combinations, we map #' # setNames() on the product: #' seq_len(3) %>% #' cross2(., ., .filter = `==`) %>% #' map(setNames, c("x", "y")) #' #' # Alternatively we can encapsulate the arguments in a named list #' # before crossing to get named components: #' seq_len(3) %>% #' list(x = ., y = .) %>% #' cross(.filter = `==`) cross <- function(.l, .filter = NULL) { if (is_empty(.l)) { return(.l) } if (!is.null(.filter)) { .filter <- as_mapper(.filter) } n <- length(.l) lengths <- lapply(.l, length) names <- names(.l) factors <- cumprod(lengths) total_length <- factors[n] factors <- c(1, factors[-n]) out <- replicate(total_length, vector("list", n), simplify = FALSE) for (i in seq_along(out)) { for (j in seq_len(n)) { index <- floor((i - 1) / factors[j]) %% length(.l[[j]]) + 1 out[[i]][[j]] <- .l[[j]][[index]] } names(out[[i]]) <- names # Filter out unwanted elements. We set them to NULL instead of # completely removing them so we don't mess up the loop indexing. # NULL elements are removed later on. if (!is.null(.filter)) { is_to_filter <- do.call(".filter", unname(out[[i]])) if (!is.logical(is_to_filter) || !length(is_to_filter) == 1) { stop("The filter function must return TRUE or FALSE", call. = FALSE) } if (is_to_filter) { out[i] <- list(NULL) } } } # Remove filtered elements compact(out) } #' @export #' @rdname cross cross2 <- function(.x, .y, .filter = NULL) { cross(list(.x, .y), .filter = .filter) } #' @export #' @rdname cross cross3 <- function(.x, .y, .z, .filter = NULL) { cross(list(.x, .y, .z), .filter = .filter) } #' @rdname cross #' @export cross_df <- function(.l, .filter = NULL) { cross(.l, .filter = .filter) %>% transpose() %>% simplify_all() %>% tibble::as_tibble() } #' @export #' @usage NULL #' @rdname cross cross_n <- function(...) { warning("`cross_n()` is deprecated; please use `cross()` instead.", call. = FALSE) cross(...) } #' @export #' @usage NULL #' @rdname cross cross_d <- function(...) { warning("`cross_d()` is deprecated; please use `cross_df()` instead.", call. = FALSE) cross_df(...) } purrr/R/map.R0000644000176200001440000001263013171452210012523 0ustar liggesusers#' Apply a function to each element of a vector #' #' @description #' #' The map functions transform their input by applying a function to #' each element and returning a vector the same length as the input. #' #' * `map()`, `map_if()` and `map_at()` always return a list. See the #' [modify()] family for versions that return an object of the same #' type as the input. #' #' The `_if` and `_at` variants take a predicate function `.p` that #' determines which elements of `.x` are transformed with `.f`. #' #' * `map_lgl()`, `map_int()`, `map_dbl()` and `map_chr()` return #' vectors of the corresponding type (or die trying). #' #' * `map_dfr()` and `map_dfc()` return data frames created by #' row-binding and column-binding respectively. They require dplyr #' to be installed. #' #' * `walk()` calls `.f` for its side-effect and returns the input `.x`. #' #' @inheritParams as_mapper #' @param .x A list or atomic vector. #' @param .p A single predicate function, a formula describing such a #' predicate function, or a logical vector of the same length as `.x`. #' Alternatively, if the elements of `.x` are themselves lists of #' objects, a string indicating the name of a logical element in the #' inner lists. Only those elements where `.p` evaluates to #' `TRUE` will be modified. #' @param .at A character vector of names or a numeric vector of #' positions. Only those elements corresponding to `.at` will be #' modified. #' @param ... Additional arguments passed on to `.f`. #' @return All functions return a vector the same length as `.x`. #' #' `map()` returns a list, `map_lgl()` a logical vector, `map_int()` an #' integer vector, `map_dbl()` a double vector, and `map_chr()` a character #' vector. The output of `.f` will be automatically typed upwards, #' e.g. logical -> integer -> double -> character. #' #' `walk()` returns the input `.x` (invisibly). This makes it easy to #' use in pipe. #' @export #' @family map variants #' @examples #' 1:10 %>% #' map(rnorm, n = 10) %>% #' map_dbl(mean) #' #' # Or use an anonymous function #' 1:10 %>% #' map(function(x) rnorm(10, x)) #' #' # Or a formula #' 1:10 %>% #' map(~ rnorm(10, .x)) #' #' # Extract by name or position #' # .default specifies value for elements that are missing or NULL #' l1 <- list(list(a = 1L), list(a = NULL, b = 2L), list(b = 3L)) #' l1 %>% map("a", .default = "???") #' l1 %>% map_int("b", .default = NA) #' l1 %>% map_int(2, .default = NA) #' #' # Supply multiple values to index deeply into a list #' l2 <- list( #' list(num = 1:3, letters[1:3]), #' list(num = 101:103, letters[4:6]), #' list() #' ) #' l2 %>% map(c(2, 2)) #' #' # Use a list to build an extractor that mixes numeric indices and names, #' # and .default to provide a default value if the element does not exist #' l2 %>% map(list("num", 3)) #' l2 %>% map_int(list("num", 3), .default = NA) #' #' # A more realistic example: split a data frame into pieces, fit a #' # model to each piece, summarise and extract R^2 #' mtcars %>% #' split(.$cyl) %>% #' map(~ lm(mpg ~ wt, data = .x)) %>% #' map(summary) %>% #' map_dbl("r.squared") #' #' # Use map_lgl(), map_dbl(), etc to reduce to a vector. #' # * list #' mtcars %>% map(sum) #' # * vector #' mtcars %>% map_dbl(sum) #' #' # If each element of the output is a data frame, use #' # map_dfr to row-bind them together: #' mtcars %>% #' split(.$cyl) %>% #' map(~ lm(mpg ~ wt, data = .x)) %>% #' map_dfr(~ as.data.frame(t(as.matrix(coef(.))))) #' # (if you also want to preserve the variable names see #' # the broom package) map <- function(.x, .f, ...) { .f <- as_mapper(.f, ...) .Call(map_impl, environment(), ".x", ".f", "list") } #' @rdname map #' @export map_if <- function(.x, .p, .f, ...) { sel <- probe(.x, .p) out <- list_along(.x) out[sel] <- map(.x[sel], .f, ...) out[!sel] <- .x[!sel] set_names(out, names(.x)) } #' @rdname map #' @export map_at <- function(.x, .at, .f, ...) { sel <- inv_which(.x, .at) out <- list_along(.x) out[sel] <- map(.x[sel], .f, ...) out[!sel] <- .x[!sel] set_names(out, names(.x)) } #' @rdname map #' @export map_lgl <- function(.x, .f, ...) { .f <- as_mapper(.f, ...) .Call(map_impl, environment(), ".x", ".f", "logical") } #' @rdname map #' @export map_chr <- function(.x, .f, ...) { .f <- as_mapper(.f, ...) .Call(map_impl, environment(), ".x", ".f", "character") } #' @rdname map #' @export map_int <- function(.x, .f, ...) { .f <- as_mapper(.f, ...) .Call(map_impl, environment(), ".x", ".f", "integer") } #' @rdname map #' @export map_dbl <- function(.x, .f, ...) { .f <- as_mapper(.f, ...) .Call(map_impl, environment(), ".x", ".f", "double") } #' @rdname map #' @param .id If not `NULL` a variable with this name will be created #' giving either the name or the index of the data frame. #' @export map_dfr <- function(.x, .f, ..., .id = NULL) { if (!is_installed("dplyr")) { abort("`map_df()` requires dplyr") } .f <- as_mapper(.f, ...) res <- map(.x, .f, ...) dplyr::bind_rows(res, .id = .id) } #' @rdname map #' @export #' @usage NULL map_df <- map_dfr #' @rdname map #' @export map_dfc <- function(.x, .f, ...) { if (!is_installed("dplyr")) { abort("`map_dfc()` requires dplyr") } .f <- as_mapper(.f, ...) res <- map(.x, .f, ...) dplyr::bind_cols(res) } #' @export #' @rdname map walk <- function(.x, .f, ...) { .f <- as_mapper(.f, ...) for (i in seq_along(.x)) { .f(.x[[i]], ...) } invisible(.x) } purrr/R/partial.R0000644000176200001440000000555213137651733013424 0ustar liggesusers#' Partial apply a function, filling in some arguments. #' #' Partial function application allows you to modify a function by pre-filling #' some of the arguments. It is particularly useful in conjunction with #' functionals and other function operators. #' #' @section Design choices: #' #' There are many ways to implement partial function application in R. #' (see e.g. `dots` in \url{https://github.com/crowding/ptools} for another #' approach.) This implementation is based on creating functions that are as #' similar as possible to the anonymous functions that you'd create by hand, #' if you weren't using `partial`. #' #' @param ...f a function. For the output source to read well, this should be a #' named function. #' @param ... named arguments to `...f` that should be partially applied. #' @param .env the environment of the created function. Defaults to #' [parent.frame()] and you should rarely need to modify this. #' @param .lazy If `TRUE` arguments evaluated lazily, if `FALSE`, #' evaluated when `partial` is called. #' @param .first If `TRUE`, the partialized arguments are placed #' to the front of the function signature. If `FALSE`, they are #' moved to the back. Only useful to control position matching of #' arguments when the partialized arguments are not named. #' @export #' @examples #' # Partial is designed to replace the use of anonymous functions for #' # filling in function arguments. Instead of: #' compact1 <- function(x) discard(x, is.null) #' #' # we can write: #' compact2 <- partial(discard, .p = is.null) #' #' # and the generated source code is very similar to what we made by hand #' compact1 #' compact2 #' #' # Note that the evaluation occurs "lazily" so that arguments will be #' # repeatedly evaluated #' f <- partial(runif, n = rpois(1, 5)) #' f #' f() #' f() #' #' # You can override this by saying .lazy = FALSE #' f <- partial(runif, n = rpois(1, 5), .lazy = FALSE) #' f #' f() #' f() #' #' # This also means that partial works fine with functions that do #' # non-standard evaluation #' my_long_variable <- 1:10 #' plot2 <- partial(plot, my_long_variable) #' plot2() #' plot2(runif(10), type = "l") partial <- function(...f, ..., .env = parent.frame(), .lazy = TRUE, .first = TRUE) { stopifnot(is.function(...f)) if (.lazy) { fcall <- substitute(...f(...)) } else { fcall <- make_call(substitute(...f), .args = list(...)) } # Pass on ... from parent function n <- length(fcall) if (!.first && n > 1) { tmp <- fcall[1] tmp[[2]] <- quote(...) tmp[seq(3, n + 1)] <- fcall[seq(2, n)] names(tmp)[seq(3, n + 1)] <- names2(fcall)[seq(2, n)] fcall <- tmp } else { fcall[[n + 1]] <- quote(...) } args <- list("..." = quote(expr = )) new_function(args, fcall, .env) } make_call <- function(f, ..., .args = list()) { if (is.character(f)) f <- as.name(f) as.call(c(f, ..., .args)) } purrr/R/prepend.R0000644000176200001440000000143013102346452013403 0ustar liggesusers#' Prepend a vector #' #' This is a companion to [append()] to help merging two #' lists or atomic vectors. `prepend()` is a clearer semantic #' signal than `c()` that a vector is to be merged at the beginning of #' another, especially in a pipe chain. #' #' @param x the vector to be modified. #' @param values to be included in the modified vector. #' @param before a subscript, before which the values are to be appended. #' @return A merged vector. #' @export #' @examples #' x <- as.list(1:3) #' #' x %>% append("a") #' x %>% prepend("a") #' x %>% prepend(list("a", "b"), before = 3) prepend <- function(x, values, before = 1) { n <- length(x) stopifnot(before > 0 && before <= n) if (before == 1) { c(values, x) } else { c(x[1:(before - 1)], values, x[before:n]) } } purrr/R/composition.R0000644000176200001440000001556013124217327014324 0ustar liggesusers#' Lift the domain of a function #' #' `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. #' #' The most important of those helpers is probably `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. #' #' @inheritParams as_vector #' @param ..f A function to lift. #' @param ... Default arguments for `..f`. These will be #' evaluated only once, when the lifting factory is called. #' @return A function. #' @name lift #' @seealso [invoke()] NULL #' @rdname lift #' @section from ... to `list(...)` or `c(...)`: #' Here dots should be taken here in a figurative way. The lifted #' functions does not need to take dots per se. The function is #' simply wrapped a function in [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. #' @param .unnamed If `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. #' @export #' @examples #' ### 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() lift <- function(..f, ..., .unnamed = FALSE) { force(..f) defaults <- list(...) function(.x = list(), ...) { if (.unnamed) { .x <- unname(.x) } do.call("..f", c(.x, defaults, list(...))) } } #' @rdname lift #' @export lift_dl <- lift #' @rdname lift #' @export lift_dv <- function(..f, ..., .unnamed = FALSE) { force(..f) defaults <- list(...) function(.x, ...) { if (.unnamed) { .x <- unname(.x) } .x <- as.list(.x) do.call("..f", c(.x, defaults, list(...))) } } #' @rdname lift #' @section from `c(...)` to `list(...)` or `...`: #' These factories allow a function taking a vector to take a list #' or dots instead. The lifted function internally transforms its #' inputs back to an atomic vector. purrr does not obey the usual R #' casting rules (e.g., `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`. #' @export #' @examples #' # #' #' #' ### Lifting from c(...) to list(...) or ... #' #' # 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") lift_vl <- function(..f, ..., .type) { force(..f) defaults <- list(...) if (missing(.type)) .type <- NULL function(.x = list(), ...) { x <- as_vector(.x, .type) do.call("..f", c(list(x), defaults, list(...))) } } #' @rdname lift #' @export lift_vd <- function(..f, ..., .type) { force(..f) defaults <- list(...) if (missing(.type)) .type <- NULL function(...) { x <- as_vector(list(...), .type) do.call("..f", c(list(x), defaults)) } } #' @rdname lift #' @section from list(...) to c(...) or ...: #' `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`. #' #' @export #' @examples #' # #' #' #' ### Lifting from list(...) to c(...) or ... #' #' # cross() 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_dots <- lift_ld(cross) #' out1 <- cross(list(a = 1:2, b = c("a", "b", "c"))) #' out2 <- cross_dots(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, .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, .) #' } #' #' \dontrun{ #' letters %>% glue() # fails because glue() expects a list} #' #' letters %>% lift_lv(glue)() # succeeds lift_ld <- function(..f, ...) { force(..f) defaults <- list(...) function(...) { do.call("..f", c(list(list(...)), defaults)) } } #' @rdname lift #' @export lift_lv <- function(..f, ...) { force(..f) defaults <- list(...) function(.x, ...) { do.call("..f", c(list(as.list(.x)), defaults, list(...))) } } purrr/R/when.R0000644000176200001440000000547613124217327012727 0ustar liggesusers#' Match/validate a set of conditions for an object and continue with the action #' associated with the first valid match. #' #' `when` is a flavour of pattern matching (or an if-else abstraction) in #' which a value is matched against a sequence of condition-action sets. When a #' valid match/condition is found the action is executed and the result of the #' action is returned. #' #' @param . the value to match against #' @param ... formulas; each containing a condition as LHS and an action as RHS. #' named arguments will define additional values. #' @keywords internal #' @return The value resulting from the action of the first valid #' match/condition is returned. If no matches are found, and no default is #' given, NULL will be returned. #' # @details condition-action sets are written as formulas with conditions as # left-hand sides and actions as right-hand sides. A formula with only a # right-hand will be treated as a condition which is always satisfied. For # such a default case one can also omit the `~` symbol, but note that its # value will then be evaluated. Any named argument will be made available in # all conditions and actions, which is useful in avoiding repeated temporary # computations or temporary assignments. # #' Validity of the conditions are tested with `isTRUE`, or equivalently #' with `identical(condition, TRUE)`. #' In other words conditions resulting in more than one logical will never #' be valid. Note that the input value is always treated as a single object, #' as opposed to the `ifelse` function. #' #' @examples #' 1:10 %>% #' when( #' sum(.) <= 50 ~ sum(.), #' sum(.) <= 100 ~ sum(.)/2, #' ~ 0 #' ) #' #' 1:10 %>% #' when( #' sum(.) <= x ~ sum(.), #' sum(.) <= 2*x ~ sum(.)/2, #' ~ 0, #' x = 60 #' ) #' #' iris %>% #' subset(Sepal.Length > 10) %>% #' when( #' nrow(.) > 0 ~ ., #' ~ iris %>% head(10) #' ) #' #' iris %>% #' head %>% #' when(nrow(.) < 10 ~ ., #' ~ stop("Expected fewer than 10 rows.")) #' @export when <- function(., ...) { dots <- list(...) names <- names(dots) named <- if (is.null(names)) rep(FALSE, length(dots)) else names != "" if (sum(!named) == 0) stop("At least one matching condition is needed.", call. = FALSE) is_formula <- vapply(dots, function(dot) identical(class(dot), "formula"), logical(1L)) env <- new.env(parent = parent.frame()) env[["."]] <- . if (sum(named) > 0) for (i in which(named)) env[[names[i]]] <- dots[[i]] result <- NULL for (i in which(!named)) { if (is_formula[i]) { action <- length(dots[[i]]) if (action == 2 || is_true(eval(dots[[i]][[2]], env, env))) { result <- eval(dots[[i]][[action]], env, env) break } } else { result <- dots[[i]] } } result } purrr/R/list-modify.R0000644000176200001440000000561713137651733014232 0ustar liggesusers#' Modify a list #' #' @description #' #' `list_modify()` and `list_merge()` recursively combine two lists, matching #' elements either by name or position. If an sub-element is present in #' both lists `list_modify()` takes the value from `y`, and `list_merge()` #' concatenates the values together. #' #' `update_list()` handles formulas and quosures that can refer to #' values existing within the input list. Note that this function #' might be deprecated in the future in favour of a `dplyr::mutate()` #' method for lists. #' #' @param .x List to modify. #' @param ... New values of a list. Use `NULL` to remove values. #' Use a formula to evaluate in the context of the list values. #' These dots have [splicing semantics][rlang::dots_list]. #' @export #' @examples #' x <- list(x = 1:10, y = 4, z = list(a = 1, b = 2)) #' str(x) #' #' # Update values #' str(list_modify(x, a = 1)) #' # Replace values #' str(list_modify(x, z = 5)) #' str(list_modify(x, z = list(a = 1:5))) #' # Remove values #' str(list_modify(x, z = NULL)) #' #' # Combine values #' str(list_merge(x, x = 11, z = list(a = 2:5, c = 3))) #' #' #' # All these functions take dots with splicing. Use !!! or UQS() to #' # splice a list of arguments: #' l <- list(new = 1, y = NULL, z = 5) #' str(list_modify(x, !!! l)) #' #' # In update_list() you can also use quosures and formulas to #' # compute new values. This function is likely to be deprecated in #' # the future #' update_list(x, z1 = ~z[[1]]) #' update_list(x, z = rlang::quo(x + y)) list_modify <- function(.x, ...) { dots <- dots_list(...) list_recurse(.x, dots, function(x, y) y) } #' @export #' @rdname list_modify list_merge <- function(.x, ...) { dots <- dots_list(...) list_recurse(.x, dots, c) } list_recurse <- function(x, y, base_case) { stopifnot(is.list(x), is.list(y)) if (is_empty(x)) { return(y) } else if (is_empty(y)) { return(x) } x_names <- names(x) y_names <- names(y) if (!is_names(x_names) && !is_names(y_names)) { for (i in rev(seq_along(y))) { if (i <= length(x) && is_list(x[[i]]) && is_list(y[[i]])) { x[[i]] <- list_recurse(x[[i]], y[[i]], base_case) } else { x[[i]] <- base_case(x[[i]], y[[i]]) } } } else if (is_names(x_names) && is_names(y_names)) { for (nm in y_names) { if (has_name(x, nm) && is_list(x[[nm]]) && is_list(y[[nm]])) { x[[nm]] <- list_recurse(x[[nm]], y[[nm]], base_case) } else { x[[nm]] <- base_case(x[[nm]], y[[nm]]) } } } else { stop("`x` and `y` must be either both named or both unnamed", call. = FALSE) } x } #' @rdname list_modify #' @export #' @usage NULL update_list <- function(.x, ...) { dots <- dots_list(...) formulas <- map_lgl(dots, is_bare_formula, lhs = FALSE, scoped = TRUE) dots <- map_if(dots, formulas, as_quosure) dots <- map_if(dots, is_quosure, eval_tidy, data = .x) list_recurse(.x, dots, function(x, y) y) } purrr/R/keep.R0000644000176200001440000000236313102346452012700 0ustar liggesusers#' Keep or discard elements using a predicate function. #' #' `keep` and `discard` are opposites. `compact` is a handy #' wrapper that removes all elements that are `NULL`. #' #' These are usually called `select` or `filter` and `reject` or #' `drop`, but those names are already taken. `keep` is similar to #' [Filter()] but the argument order is more convenient, and the #' evaluation of `.f` is stricter. #' #' @param .x A list or vector. #' @param ... Additional arguments passed on to `.p`. #' @inheritParams map_if #' @export #' @examples #' rep(10, 10) %>% #' map(sample, 5) %>% #' keep(function(x) mean(x) > 6) #' #' # Or use a formula #' rep(10, 10) %>% #' map(sample, 5) %>% #' keep(~ mean(.x) > 6) #' #' # Using a string instead of a function will select all list elements #' # where that subelement is TRUE #' x <- rerun(5, a = rbernoulli(1), b = sample(10)) #' x #' x %>% keep("a") #' x %>% discard("a") keep <- function(.x, .p, ...) { sel <- probe(.x, .p, ...) .x[!is.na(sel) & sel] } #' @export #' @rdname keep discard <- function(.x, .p, ...) { sel <- probe(.x, .p, ...) .x[is.na(sel) | !sel] } #' @export #' @rdname keep compact <- function(.x, .p = identity) { .f <- as_mapper(.p) .x %>% discard(function(x) is_empty(.f(x))) } purrr/R/coercion.R0000644000176200001440000000567113102346452013562 0ustar liggesusers#' Coerce a list to a vector #' #' `as_vector()` collapses a list of vectors into one vector. It #' checks that the type of each vector is consistent with #' `.type`. If the list can not be simplified, it throws an error. #' `simplify` will simplify a vector if possible; `simplify_all` #' will apply `simplify` to every element of a list. #' #' `.type` can be a vector mold specifying both the type and the #' length of the vectors to be concatenated, such as `numeric(1)` #' or `integer(4)`. Alternatively, it can be a string describing #' the type, one of: "logical", "integer", "double", "complex", #' "character" or "raw". #' #' @param .x A list of vectors #' @param .type A vector mold or a string describing the type of the #' input vectors. The latter can be any of the types returned by #' [typeof()], or "numeric" as a shorthand for either #' "double" or "integer". #' @export #' @examples #' # Supply the type either with a string: #' as.list(letters) %>% as_vector("character") #' #' # Or with a vector mold: #' as.list(letters) %>% as_vector(character(1)) #' #' # Vector molds are more flexible because they also specify the #' # length of the concatenated vectors: #' list(1:2, 3:4, 5:6) %>% as_vector(integer(2)) #' #' # Note that unlike vapply(), as_vector() never adds dimension #' # attributes. So when you specify a vector mold of size > 1, you #' # always get a vector and not a matrix as_vector <- function(.x, .type = NULL) { if (can_simplify(.x, .type)) { unlist(.x) } else { stop("Cannot coerce .x to a vector", call. = FALSE) } } #' @export #' @rdname as_vector simplify <- function(.x, .type = NULL) { if (can_simplify(.x, .type)) { unlist(.x) } else { .x } } #' @export #' @rdname as_vector simplify_all <- function(.x, .type = NULL) { map(.x, simplify, .type = .type) } # Simplify a list of atomic vectors of the same type to a vector # # simplify_list(list(1, 2, 3)) can_simplify <- function(x, type = NULL) { is_atomic <- vapply(x, is.atomic, logical(1)) if (!all(is_atomic)) return(FALSE) mode <- unique(vapply(x, typeof, character(1))) if (length(mode) > 1 && !all(c("double", "integer") %in% mode)) { return(FALSE) } # This can be coerced safely. If type is supplied, perform # additional check is.null(type) || can_coerce(x, type) } can_coerce <- function(x, type) { actual <- typeof(x[[1]]) if (is_mold(type)) { lengths <- unique(map_int(x, length)) if (length(lengths) > 1 || !(lengths == length(type))) { return(FALSE) } else { type <- typeof(type) } } if (actual == "integer" && type %in% c("integer", "double", "numeric")) { return(TRUE) } if (actual %in% c("integer", "double") && type == "numeric") { return(TRUE) } actual == type } # is a mold? As opposed to a string is_mold <- function(type) { modes <- c("numeric", "logical", "integer", "double", "complex", "character", "raw") length(type) > 1 || (!type %in% modes) } purrr/R/purrr.R0000644000176200001440000000013413102444774013126 0ustar liggesusers#' @keywords internal #' @import rlang #' @useDynLib purrr, .registration = TRUE "_PACKAGE" purrr/R/compose.R0000644000176200001440000000076013102346452013420 0ustar liggesusers#' Compose multiple functions #' #' @param ... n functions to apply in order from right to left. #' @return A function #' @export #' @examples #' not_null <- compose(`!`, is.null) #' not_null(4) #' not_null(NULL) #' #' add1 <- function(x) x + 1 #' compose(add1, add1)(8) compose <- function(...) { fs <- lapply(list(...), match.fun) n <- length(fs) last <- fs[[n]] rest <- fs[-n] function(...) { out <- last(...) for (f in rev(rest)) { out <- f(out) } out } } purrr/R/set_names.R0000644000176200001440000000203513124216350013723 0ustar liggesusers#' @title Set names in a vector #' #' @details #' This is a snake case wrapper for [stats::setNames()], with #' tweaked defaults, and stricter argument checking. #' #' @usage set_names(x, nm = x, ...) #' @param x Vector to name #' @param nm,... Vector of names, the same length as `x`. #' #' You can specify names in three ways: #' #' * If you do nothing, `x` will be named with itself #' #' * You can supply either a character vector to `nm` or individual #' strings in to `...`` #' #' * If `x` already has names, you can provide a function or formula #' to transform the existing names. #' #' @return `.x` with the names attribute set. #' @export #' @examples #' set_names(1:4, c("a", "b", "c", "d")) #' set_names(1:4, letters[1:4]) #' set_names(1:4, "a", "b", "c", "d") #' #' # If the second argument is ommitted a vector is named with itself #' set_names(letters[1:5]) #' #' # Alternatively you can supply a function #' set_names(1:10, ~ letters[seq_along(.)]) #' set_names(head(mtcars), toupper) #' @name set_names rlang::set_names purrr/R/negate.R0000644000176200001440000000076613102451525013222 0ustar liggesusers#' Negate a predicate function. #' #' @inheritParams map_if #' @inheritParams as_mapper #' @return A new predicate function. #' @export #' @examples #' negate("x") #' negate(is.null) #' negate(~ .x > 0) #' #' x <- transpose(list(x = 1:10, y = rbernoulli(10))) #' x %>% keep("y") %>% length() #' x %>% keep(negate("y")) %>% length() #' # Same as #' x %>% discard("y") %>% length() negate <- function(.p, .default = FALSE) { .p <- as_mapper(.p) body(.p) <- expr({ ! ( !! body(.p) ) }) .p } purrr/R/every-some.R0000644000176200001440000000136113102346452014044 0ustar liggesusers#' Do every or some elements of a list satisfy a predicate? #' #' @inheritParams map_if #' @inheritParams map #' @return A logical vector of length 1. #' @export #' @examples #' x <- list(0, 1, TRUE) #' x %>% every(identity) #' x %>% some(identity) #' #' y <- list(0:10, 5.5) #' y %>% every(is.numeric) #' y %>% every(is.integer) every <- function(.x, .p, ...) { .p <- as_mapper(.p, ...) for (i in seq_along(.x)) { val <- .p(.x[[i]], ...) if (is_false(val)) return(FALSE) if (anyNA(val)) return(NA) } TRUE } #' @export #' @rdname every some <- function(.x, .p, ...) { .p <- as_mapper(.p, ...) for (i in seq_along(.x)) { val <- .p(.x[[i]], ...) if (is_true(val)) return(TRUE) if (anyNA(val)) return(NA) } FALSE } purrr/R/transpose.R0000644000176200001440000000366113123217241013770 0ustar liggesusers#' Transpose a list. #' #' Transpose turns a list-of-lists "inside-out"; it turns a pair of lists into a #' list of pairs, or a list of pairs into pair of lists. For example, #' if you had a list of length n where each component had values `a` and #' `b`, `transpose()` would make a list with elements `a` and #' `b` that contained lists of length n. It's called transpose because #' \code{x[[1]][[2]]} is equivalent to \code{transpose(x)[[2]][[1]]}. #' #' Note that `transpose()` is its own inverse, much like the #' transpose operation on a matrix. You can get back the original #' input by transposing it twice. #' #' @param .l A list of vectors to zip. The first element is used as the #' template; you'll get a warning if a sub-list is not the same length as #' the first element. #' @param .names For efficiency, `transpose()` usually inspects the #' first component of `.l` to determine the structure. Use `.names` #' if you want to override this default. #' @return A list with indexing transposed compared to `.l`. #' @export #' @examples #' x <- rerun(5, x = runif(1), y = runif(5)) #' x %>% str() #' x %>% transpose() %>% str() #' # Back to where we started #' x %>% transpose() %>% transpose() %>% str() #' #' # transpose() is useful in conjunction with safely() & quietly() #' x <- list("a", 1, 2) #' y <- x %>% map(safely(log)) #' y %>% str() #' y %>% transpose() %>% str() #' #' # Use simplify_all() to reduce to atomic vectors where possible #' x <- list(list(a = 1, b = 2), list(a = 3, b = 4), list(a = 5, b = 6)) #' x %>% transpose() #' x %>% transpose() %>% simplify_all() #' #' # Provide explicit component names to prevent loss of those that don't #' # appear in first component #' ll <- list( #' list(x = 1, y = "one"), #' list(z = "deux", x = 2) #' ) #' ll %>% transpose() #' nms <- ll %>% map(names) %>% reduce(union) #' ll %>% transpose(.names = nms) transpose <- function(.l, .names = NULL) { .Call(transpose_impl, .l, .names) } purrr/R/predicates.R0000644000176200001440000000310513125254534014076 0ustar liggesusers#' Test is an object is integer or double #' #' Numeric is used in three different ways in base R: #' * as an alias for double (as in [as.numeric()]) #' * to mean either integer or double (as in [mode()]) #' * for something representable as numeric (as in [as.numeric()]) #' This function tests for the second, which is often not what you want #' so these functions are deprecated. #' #' @export #' @keywords internal is_numeric <- function(x) { warning("Deprecated", call. = FALSE) is_integer(x) || is_double(x) } #' @export #' @rdname is_numeric is_scalar_numeric <- function(x) { warning("Deprecated", call. = FALSE) is_scalar_integer(x) || is_scalar_double(x) } # Re-exports from purrr --------------------------------------------------- #' @export rlang::is_bare_list #' @export rlang::is_bare_atomic #' @export rlang::is_bare_vector #' @export rlang::is_bare_double #' @export rlang::is_bare_integer #' @export rlang::is_bare_numeric #' @export rlang::is_bare_character #' @export rlang::is_bare_logical #' @export rlang::is_list #' @export rlang::is_atomic #' @export rlang::is_vector #' @export rlang::is_integer #' @export rlang::is_double #' @export rlang::is_character #' @export rlang::is_logical #' @export rlang::is_null #' @export rlang::is_function #' @export rlang::is_scalar_list #' @export rlang::is_scalar_atomic #' @export rlang::is_scalar_vector #' @export rlang::is_scalar_double #' @export rlang::is_scalar_character #' @export rlang::is_scalar_logical #' @export rlang::is_scalar_integer #' @export rlang::is_empty #' @export rlang::is_formula purrr/R/lmap.R0000644000176200001440000000714013171415127012705 0ustar liggesusers#' Apply a function to list-elements of a list #' #' `lmap()`, `lmap_at()` and `lmap_if()` are similar to #' `map()`, `map_at()` and `map_if()`, with the #' difference that they operate exclusively on functions that take #' \emph{and} return a list (or data frame). Thus, instead of mapping #' the elements of a list (as in \code{.x[[i]]}), they apply a #' function `.f` to each subset of size 1 of that list (as in #' `.x[i]`). We call those those elements `list-elements`). #' #' Mapping the list-elements `.x[i]` has several advantages. It #' makes it possible to work with functions that exclusively take a #' list or data frame. It enables `.f` to access the attributes #' of the encapsulating list, like the name of the components it #' receives. It also enables `.f` to return a larger list than #' the list-element of size 1 it got as input. Conversely, `.f` #' can also return empty lists. In these cases, the output list is #' reshaped with a different size than the input list `.x`. #' @param .x A list or data frame. #' @param .f A function that takes and returns a list or data frame. #' @inheritParams map_if #' @inheritParams map_at #' @inheritParams map #' @return If `.x` is a list, a list. If `.x` is a data #' frame, a data frame. #' @family map variants #' @export #' @examples #' # Let's write a function that returns a larger list or an empty list #' # depending on some condition. This function also uses the names #' # metadata available in the attributes of the list-element #' maybe_rep <- function(x) { #' n <- rpois(1, 2) #' out <- rep_len(x, n) #' if (length(out) > 0) { #' names(out) <- paste0(names(x), seq_len(n)) #' } #' out #' } #' #' # The output size varies each time we map f() #' x <- list(a = 1:4, b = letters[5:7], c = 8:9, d = letters[10]) #' x %>% lmap(maybe_rep) #' #' # We can apply f() on a selected subset of x #' x %>% lmap_at(c("a", "d"), maybe_rep) #' #' # Or only where a condition is satisfied #' x %>% lmap_if(is.character, maybe_rep) #' #' #' # A more realistic example would be a function that takes discrete #' # variables in a dataset and turns them into disjunctive tables, a #' # form that is amenable to fitting some types of models. #' #' # A disjunctive table contains only 0 and 1 but has as many columns #' # as unique values in the original variable. Ideally, we want to #' # combine the names of each level with the name of the discrete #' # variable in order to identify them. Given these requirements, it #' # makes sense to have a function that takes a data frame of size 1 #' # and returns a data frame of variable size. #' disjoin <- function(x, sep = "_") { #' name <- names(x) #' x <- as.factor(x[[1]]) #' #' out <- lapply(levels(x), function(level) { #' as.numeric(x == level) #' }) #' #' names(out) <- paste(name, levels(x), sep = sep) #' tibble::as_tibble(out) #' } #' #' # Now, we are ready to map disjoin() on each categorical variable of a #' # data frame: #' iris %>% lmap_if(is.factor, disjoin) #' mtcars %>% lmap_at(c("cyl", "vs", "am"), disjoin) lmap <- function(.x, .f, ...) { .x %>% lmap_at(seq_along(.x), .f, ...) } #' @rdname lmap #' @export lmap_if <- function(.x, .p, .f, ...) { sel <- probe(.x, .p) %>% which() .x %>% lmap_at(sel, .f, ...) } #' @rdname lmap #' @export lmap_at <- function(.x, .at, .f, ...) { if (is_formula(.f)) { .f <- as_mapper(.f, ...) } sel <- inv_which(.x, .at) out <- vector("list", length(.x)) for (i in seq_along(.x)) { res <- if (sel[[i]]) { .f(.x[i], ...) } else { .x[i] } stopifnot(is.list(res)) out[[i]] <- res } flatten(out) %>% maybe_as_data_frame(.x) } purrr/vignettes/0000755000176200001440000000000013171650761013443 5ustar liggesuserspurrr/vignettes/other-langs.Rmd0000644000176200001440000000406013124217327016325 0ustar liggesusers--- title: "Functional programming in other languages" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Functional programming in other languages} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- purrr draws inspiration from many related tools: * List operations defined in the Haskell [prelude][haskell] * Scala's [list methods][scala]. * Functional programming librarys for javascript: [underscore.js](http://underscorejs.org), [lodash](https://lodash.com) and [lazy.js](http://danieltao.com/lazy.js/). * [rlist](http://renkun.me/rlist/), another R package to support working with lists. Similar goals but somewhat different philosophy. However, the goal of purrr is not to try and simulate a purer functional programming language in R; we don't want to implement a second-class version of Haskell in R. The goal is to give you similar expressiveness to an FP language, while allowing you to write code that looks and works like R: * Instead of point free (tacit) style, we use the pipe, `%>%`, to write code that can be read from left to right. * Instead of currying, we use `...` to pass in extra arguments. * Anonymous functions are verbose in R, so we provide two convenient shorthands. For unary functions, `~ .x + 1` is equivalent to `function(.x) .x + 1`. For chains of transformations functions, `. %>% f() %>% g()` is equivalent to `function(.) . %>% f() %>% g()` (this shortcut is provided by magrittr). * R is weakly typed, so we need `map` variants that describe the output type (like `map_int()`, `map_dbl()`, etc) because don't the return type of `.f`. * R has named arguments, so instead of providing different functions for minor variations (e.g. `detect()` and `detectLast()`) we use a named argument, `.right`. Type-stable functions are easy to reason about so additional arguments will never change the type of the output. [scala]:http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.List [haskell]:http://hackage.haskell.org/package/base-4.7.0.1/docs/Prelude.html#g:11 purrr/README.md0000644000176200001440000000447513147024610012713 0ustar liggesusers purrr ====================================================== [![CRAN\_Status\_Badge](http://www.r-pkg.org/badges/version/purrr)](http://cran.r-project.org/package=purrr) [![Build Status](https://travis-ci.org/tidyverse/purrr.svg?branch=master)](https://travis-ci.org/tidyverse/purrr) [![Coverage Status](https://img.shields.io/codecov/c/github/tidyverse/purrr/master.svg)](https://codecov.io/github/tidyverse/purrr?branch=master) Overview -------- purrr enhances R's functional programming (FP) toolkit by providing a complete and consistent set of tools for working with functions and vectors. If you've never heard of FP before, the best place to start is the family of `map()` functions which allow you to replace many for loops with code that is both more succinct and easier to read. The best place to learn about the `map()` functions is the [iteration chapter](http://r4ds.had.co.nz/iteration.html) in R for data science. Installation ------------ ``` r # The easiest way to get purrr is to install the whole tidyverse: install.packages("tidyverse") # Alternatively, install just purrr: install.packages("purrr") # Or the the development version from GitHub: # install.packages("devtools") devtools::install_github("tidyverse/purrr") ``` Usage ----- The following example uses purrr to solve a fairly realistic problem: split a data frame into pieces, fit a model to each piece, compute the summary, then extract the R2. ``` r library(purrr) mtcars %>% split(.$cyl) %>% # from base R map(~ lm(mpg ~ wt, data = .)) %>% map(summary) %>% map_dbl("r.squared") #> 4 6 8 #> 0.5086326 0.4645102 0.4229655 ``` This example illustrates some of the advantages of purrr functions over the equivalents in base R: - The first argument is always the data, so purrr works naturally with the pipe. - All purrr functions are type-stable. They always return the advertised output type (`map()` returns lists; `map_dbl()` returns double vectors), or they throw an errror. - All `map()` functions either accept function, formulas (used for succinctly generating anonymous functions), a character vector (used to extract components by name), or a numeric vector (used to extract by position). purrr/MD50000644000176200001440000001517613171733547011761 0ustar liggesusers059324d1413bf11790eebfcc85afa281 *DESCRIPTION d32239bcb673463ab874e80d47fae504 *LICENSE b8b691d3ec56c2995d0eed7679ea7311 *NAMESPACE 461cec5f26550acd7a7153013ca2d401 *NEWS.md d7434232639ef496b59a8863d526f4d5 *R/along.R 48b5ad68f9c3a686e0e5d78499feeec7 *R/arrays.R dfc2fc05065d029dfbf897c83b347bd2 *R/as_mapper.R f0ec1ea254e478033f6d34fb33405499 *R/coerce.R 78c61636c1cf9295234fd97e97662b5c *R/coercion.R 7cad8d9769011f8e8becd44c0c046905 *R/compose.R 60109119d2926aa5eb8d0b25995b7103 *R/composition.R 5a1819f7c6a77d026fd8a8dd85af818d *R/cross.R 42de484120cddf450bfaefe35ea0a2be *R/depth.R fb21301f998ee034966f53c6a46453ad *R/every-some.R 4de7e21a61ef6ef0fa1a00c2522d2547 *R/find-position.R 2dd0023bfec19b388810951407f922c9 *R/flatten.R aa1335a2e02edc206e9388d0f002125f *R/head-tail.R 8fcc2f91aed2123309302e0acad47189 *R/imap.R 72aad2226d57cf651c1fc9e25af870bd *R/invoke.R 4fc05f9f9e735ec87fb0f64bcaecde91 *R/keep.R bebdbf11ecf1702b245387b214e1e46c *R/list-modify.R 36e036bfe9a070a893b9dda9f0cf1d09 *R/lmap.R fc88d69357fe258dd5cdfebe943c619d *R/map.R b84099b9b82c8ba315f0d8599bb3c4c1 *R/map2-pmap.R e420126f44f750c89a79894bef58d9fe *R/modify.R a848e46817874cd07d6f725c7fd112f2 *R/negate.R ee7ea8075ca7b0f200e8cdbb6947f303 *R/output.R 46e6971d5cab3972e96f537c953d1ccb *R/partial.R 29a5243a6ac22ef61660fa10619167aa *R/predicates.R b35704dde8e2ca4e17c830d75a1c44fd *R/prepend.R ab9be6571c4276e9e8260d553248d8b1 *R/purrr.R e19e96be648daac4265a5da6bd4cdc2b *R/reduce.R 8e41a48c6995da83d85d3407bde4477d *R/rerun.R 421ca7ab2db6ed008fba8b5558674827 *R/set_names.R 35b5580292c6c4227b218128bdd7da85 *R/splice.R f6afdc22a2adf80cede2a04ec0536488 *R/transpose.R b5ac331a5179d8aeae4b9e70cf003177 *R/utils.R 3011f46c733271400987666dc871373d *R/when.R d00538240fc18c7c44547f5fb7ab04ad *README.md ba009ec33d50810392dbb70cf5b9b5dc *build/vignette.rds d0a12786bea51ec8b9d57563e6edacd8 *inst/doc/other-langs.Rmd bf6d0d378ac18f2b89f70758cd62aedc *inst/doc/other-langs.html a1af9c3fdda713bc8e76b3049e14ec9c *man/accumulate.Rd 31bf146d7265441ae4e72caa8c366f6c *man/along.Rd a5fb7427a5582b7f26fb4628b0285fb3 *man/array-coercion.Rd 7bf23b591562d3cdb2dd0015350c2a08 *man/as_mapper.Rd e7f26ac9b0e6831a8e74e8bb6c4c0849 *man/as_vector.Rd ebc8d00b74c57e535a75097d5b3efb73 *man/compose.Rd 1079d4e27459673cef82d40c1c3ff770 *man/cross.Rd 768621965a402f6899719bed8b04dfa0 *man/detect.Rd ed53201c56c68c61eaa8cba9836027dc *man/every.Rd 1d60cfe447b30937913953382e194602 *man/figures/logo.png f936d540f0f4c19df702b72e57b1db0f *man/flatten.Rd 2d83b8c056befed23e23c5fa0a7e2901 *man/get-attr.Rd 45f2c89c9029e869a44213e3ecfdb393 *man/has_element.Rd 99e9c4dc80463cc0440f53cd30950239 *man/head_while.Rd 72c05631b758873c13c62dd9fffc9ea4 *man/imap.Rd 8d8d59bf99551df5138fdc384f0764bb *man/invoke.Rd f4654f144511be7d26d7e88f01c5c5c4 *man/is_numeric.Rd ed663b2e2ea63c371e2aae84526b8bbf *man/keep.Rd aaf7edad66c589562b6542de0ab1fa14 *man/lift.Rd 91c2686573f852772455c3173a1d0730 *man/list_modify.Rd 7cf636dd7f4135242360f83bc6bcb2de *man/lmap.Rd b59015aef9c5662cdc8664a444e67fd2 *man/map.Rd 3730114285585c293188cf3472480a42 *man/map2.Rd a5a848f017dc57f6a32984a0be5688c9 *man/modify.Rd c6f50cc4df88e89d974ae859659ac48a *man/negate.Rd 41960071b98ae08db2626bf0176474ad *man/null-default.Rd fff26baafa6d12a021a1eccc418b98d1 *man/partial.Rd a64a7ea44fcaa33c2d3ad0f7909cbc3e *man/pipe.Rd 6afcb054faa852df21065f001191d6b8 *man/pluck.Rd c2e38b7a30f26c4ea0435141446254e4 *man/prepend.Rd 1d2d8fea562ac7cf11eca1c642a81a6b *man/purrr-package.Rd f34d8370301c799a28aa8307b6394911 *man/rbernoulli.Rd 2df5a87d08db6c4b73447fa625ea444d *man/rdunif.Rd a899f7774548591e1c94c14c593b42cc *man/reduce.Rd 50df6a47659d7e3831a580ff7b008b27 *man/reexports.Rd 028392c9b567bf0e18039256b65d54df *man/rerun.Rd ed9a095395b1cf9025495309eb307ede *man/safely.Rd 1833c1e1d1e45f2a8fa6843672c92a8e *man/set_names.Rd d198d9d3ffebb0bdca5968a34b70ccf4 *man/splice.Rd 987b0b3f1704943ddbad57e9e2c21e8d *man/transpose.Rd 5cf4da2f617677a3ab17aea0e6dcfcfd *man/vec_depth.Rd 476045071aee1d250fccf59f5bab87f9 *man/when.Rd ff2850c9a8f09363bbcb7439f1a55e70 *src/backports.c 3a1ddd472e6b17c3679467cbd3409b10 *src/backports.h 74a87782ba250e74b5d1830e8864fb84 *src/coerce.c 6c50acdeb1bbbf26649665739256d211 *src/coerce.h badf005339bec33e87c784155c10f8a3 *src/extract.c 9106493550bd308acceb970096f8a587 *src/flatten.c 829d93cfd9ef1890b0c06c402f5cb8ce *src/init.c a516fbb25d2cce016910df6950e1ff4a *src/map.c daa7efc01a8489ded96d8c0863e69b0f *src/map.h 7a43db39af0dbbbe838dd5204ee7e29a *src/transpose.c 8e9d16c5c6aedcc157783b13df5b9db0 *tests/testthat.R ef7812928b1473706a682fd6948e9f98 *tests/testthat/test-along.R dcbf48a2fbdba7f91e3880f7a0064701 *tests/testthat/test-arrays.R f27cabfbbf69b64d662b72644ea39538 *tests/testthat/test-as-mapper.R 59bad9250777fd79da0f44408d7191d2 *tests/testthat/test-coerce.R 297f88674faf22665a960389c8fda450 *tests/testthat/test-compose.R b3708b23403b14b7c15db43e810029e8 *tests/testthat/test-composition.R ca3d5a991b34b91ec6cff68a6884cad1 *tests/testthat/test-cross.R c548d3237286977e949b2b665b8a5792 *tests/testthat/test-depth.R a22e930c9691c0655ba9c5fe94866adc *tests/testthat/test-every-some.R 14532ea09ed8dfca0d19e65a4f1d7c41 *tests/testthat/test-find-position.R 6857d64342f75696d507790e01efef80 *tests/testthat/test-flatten.R fb8adbe7e936c917da7db33858f2df4a *tests/testthat/test-head-tail.R d4c61639a083a3ec2dddc8a7cf632343 *tests/testthat/test-imap.R 5ffc01118f1e0f07b9c0d90cfcdce1cb *tests/testthat/test-invoke.R 1aad6ffcce7a01de2c838b1f475bd78d *tests/testthat/test-list-modify-update.R 325bad5b7fa5a5a642da9ce50753e437 *tests/testthat/test-lmap.R c70e2e34a59f3a5986b1f2ba2a0128a3 *tests/testthat/test-map.R 2b67c975d77b2e433f0ad8ea693e0e47 *tests/testthat/test-map2.R 8c865f413f11c6a265d15e1f842df9b4 *tests/testthat/test-map_n.R 93bd710e49b0ae1af8dd27d021ddc39d *tests/testthat/test-modify.R ce1638acb8d41d5bfe2fc29877484b8a *tests/testthat/test-negate.R 192dfcaa7553057ad63e307c8a474e5b *tests/testthat/test-output.R d772931501f353938cd4f19fef56df05 *tests/testthat/test-partial.R a3d9e4801e73a775b96e6e3c433285e5 *tests/testthat/test-pluck.R ea532e7f3b213aff3ad4f66c6a23b7b4 *tests/testthat/test-predicates.R b91718777144b4a6ad6d8ca71585a67c *tests/testthat/test-prepend.R 98068929576937ae3a307a5daa19a9fb *tests/testthat/test-recycle_args.R 273e7c7e8c27c6d25e8dae194f435de6 *tests/testthat/test-reduce.R e34255b41581de0f6faf55eadd8cfbdb *tests/testthat/test-rerun.R 0eb43c0afc550daeead9401117b73f03 *tests/testthat/test-simplify.R 3f87bdf05305f94b2b5012ecdaabbb30 *tests/testthat/test-splice.R 0d12dc2b093f55b36f46c31a4c69dacb *tests/testthat/test-transpose.R 6069a05aa8cef11b1ca0a42d6067e7cb *tests/testthat/test-utils.R 7125b68b593f2e86140faabd4e8586ee *tests/testthat/test-when.R d0a12786bea51ec8b9d57563e6edacd8 *vignettes/other-langs.Rmd purrr/build/0000755000176200001440000000000013171650761012532 5ustar liggesuserspurrr/build/vignette.rds0000644000176200001440000000034113171650761015067 0ustar liggesusersmP0 ?Ix)/x%!cxIv}k= 3.1) Imports: magrittr (>= 1.5), rlang (>= 0.1), tibble Suggests: covr, dplyr (>= 0.4.3), knitr, rmarkdown, testthat VignetteBuilder: knitr LazyData: true RoxygenNote: 6.0.1 NeedsCompilation: yes Packaged: 2017-10-18 13:06:58 UTC; hadley Author: Lionel Henry [aut, cre], Hadley Wickham [aut], RStudio [cph, fnd] Maintainer: Lionel Henry Repository: CRAN Date/Publication: 2017-10-18 20:19:51 UTC purrr/man/0000755000176200001440000000000013171451356012205 5ustar liggesuserspurrr/man/figures/0000755000176200001440000000000013147024610013641 5ustar liggesuserspurrr/man/figures/logo.png0000644000176200001440000007453313147024610015323 0ustar liggesusersPNG  IHDRxb]esRGB pHYs  iTXtXML:com.adobe.xmp Adobe ImageReady 1 ).=@IDATxUOrI {'TG2:vl(*b/ v+(R @( SkΝ͝;3:w+]|̉W62~֠M53gGf4;/ 7~?sf|Φ;ԸJ*>nn4~fWJ}zW4VO,)n_4tI1RW_9ƽߺޞ޾Ɔֆ {{O]>vow˞^ղ[>ҙHcQY"%Y}}ؾT mĭ6ciAFZeaQ}à͝$n%J,uHZ}Mj/H})ːx'" >+"v?`m{+JORXH}|c[~})z97w-2;{SΎ>`z}]J%΂P]ilW?߰x#7PuOڗ.Kbqغ}ctvm驫o =@u__`^Sgu-iJjʔ!?vGt56 j%ocN{⠃&Ǝ׿WĨc)y4Umu}•Oo]#_hk?V-L59aƀC· H-틾b>2:!j7*R boڸ)nO66,u1HUf㛾Y%h(v~?k s$T_vvU;{Dq1yd::; q{{zolhLds*2js{l>#>)ʺ>Me¿J @M1xRo|dig$Z/#^߿m1{IN '%>C]#H=UB_`O<>?~ۗ};=fN>>@V2ز'7${{ۣ. nz^#__kQkJ;+B$:RWLvKcĈQHZg(J9nRJUx^{_vv1b5?_o#3l_*t>/I[W˩d: un Aq Vo_WwoW}uZ,>g&N5zzz Ti|+嶨UDWmn h G?Y|$8Ѿc B\Ϸf¿Jv9&7/?|` ]޾&P#OĶ%gx˛샑nl˳#mjJe7D2K"'P1QI>"}(^6G?L߾"F;6z:v=ǕO=`"a϶x3~u {)ر-ydeLg\|c_::Dg;}V-E{%@Het* ӓYWUց@Hh7!D" _޺u[~K>XL5$ l޶hŢo ?ܵO?;&\kO_UcB7s+QwHH.> [U{JI'F.ԱA!W*q-*> \MψrIkz*/RUJ V]wFlT;3}|̟XPjdly&%_^7}Ȟ ЩR׸4u{>xk^3fJDwtl^ %V ̅"TxJLFW3 ? =;6a765"jÒQǗ|o&|[Y_~k: AWrj4T =^yJ-?0[2(I%SҠNMr)ɩ$ $K2W P.nT@!SU%Agl +!xy"jW"S%`$K{DK&PJePc$:n-/m5ͨ5h`'5n6ĭ]XhiG|{S=}y/J`~qxq҅pa i8+&]YIjstFLEJ%HQ i˭h;8:dIt'+)M=i9$e}fSaOáK.{Ϡ؍  KnK(v%_Ev:w>x;3v?~"xK 3g/gAR]DR*1OJ "DDH`8(.]" /ZmXBt;D0:ࢨmG:azO=X'We|#qny1-=/N'=b9Ò?~ a[ԨaOyGVT[,H`J$ȃ``PiL'RscmpL&{[cS%SfYM*Rxy?RP~G~kMtv%qտ#wCV%u˟ܲ!#Gq/ :G2A"t7 ?bi#G|+#>r4HdnOAN$?b@K| D" !{>5s/94JT/$SؙHYi֞MCC'iRc4WT6osS8,9aI+aɕd?㛋ӲdZ}Njx oZ.:g?g`g'NIH$e&BD8)-%x)^`oE$gIkbJ9ֶ7< &@{ɟ,dgKO^T8ZiZ|̈WVydJzlsS}],Y$~_?qRۭ;t#KKa.ybӵSv\o;/ƛM %ܿeC; չP?"ўQ;0rr\şJAꓘʕ VJT%7ubm۶w{͞{͚㦛EI\@cǩg̉G4~unq)$=@wb;*i*_4^uΉaXtq|SY}wOtćM.y:[o׼gYzj`h@p(@ @}E⸶q}'>//8pUǴQfG{AJ}br>Qf}QhMZx߽)L B;m`-~r\W+6o럷_3N~E;#7mo|K TӁcׯ_Skh Қē͏o{"i܄\]cFM;~3: NHV<Й?g}N׿(Իڋ䀩^Nj69k /10|B33l؈xk_rhh / *;67wnm>Wp'c{#Gb}iN9m,:z u54 2~Iܗ3w73TKGk~{C\ybrVf߈xzO^CڀZd2xHzqg[;f\2ÎdQ#*ڨn3XUjX"Damڴ>7|}S{u[W.&442YMқʱ4 3|gֱܴJsݸT0K[Xj:645Ƽ1}xx~ʘsQx L- .'Y7_y*L`ҶDso}롓#nᮜ[7༗U/;QA=(+WYLl[52 o;;wVGsc;Aw?SGOknϥ5YЅAP}誹$O6伮P%~S$yTO4CrZusN^?tVjL{3'|RP*TLFKH1Ӏ*C>*,lEklZH ~yq5gk7bJ{&?ѨD=q1ЏJ p]nZm'&<-s p6vμtkbfK,{Gtj:E(زusJN/"-sM\x jLTjZwf2Kb 9f6˃ mLk)X\gK"d\ȢezKEs)>Gt\l~.NUƷ~RS8K14 {k[lhAK'tCHP> !r5氳+/z*Xkd Z/29C)1O{w}G6_kkqZ虸⇗slWZrȼ~j0V%vd,ó diq$|_.;>KQ^?f_׃zɴ7 ^C'꨿) r;8uף0^)}'ᬺhIBrލT''AsLߔLʱx|PE{1T?2 #}`e@gǽޝg]Wo]1g1ykW^c5=}o9ː1`!͚RSK#ܨ4(Z65a2kA.Z$\-sxL΃[B5Tc鑍*`ʵu/yMV³.a~>c q9= مyّq.% b2%`-o͂ѓ/ {_·$uk֬(_4C۷\RRd7-Y7)>0%p7Qi E6V!12xFtܑ9˲YjEU5]WtDp@۾jKm@@p< 9/g;fK%*KF2`geonuk>8x1Ӧ*'^74[wykY:~hQW_fƗYiԘ1ca/ u(ab?} c9{#B>7#N94Ln貰D><؎Ec|\Y޲DE^S&OiCmA|E|C5RtCmQ'0(ɞK$0$_J8>K&˧ ݪw$k|ʥ_dVάۜgX`pN}]g;٨iIKmUmI6]RK8f3%O[h i/v1nmT;!<@ ^j lO&Y0L{'$lL@T'LhF`'Ra^çP6"ZsQ\eY ESr 'MPeg}[8Trs9قs]THMc w6H<$ t> 9#&L3*;!g-pQԟ | ć>06TrxIm"SRC= C(S밹,Gev ؅AOąϭ e8UGS,ުylɀSHҎO LN Ny(r,,LJ2ba"-PU?xv5K0IY$SewGul9⅒N/]΀ ~ͬ"0ud2tBFDp`:9PҤ@W֖Ϩam,^)f `4^a^]а(27f}i6 8GfO\{<͚Nn$l,.zx;IV(R@,/ SliI,sN(mW\sC5(%Sgm3Ӌ*5B^̪mX0U\RYWFиox2`~%ռ @ @d$uNuwg=JnYE )HD L'oxCN<9# ,8JᱝX%oOf541!#LD*6QLғJXj.By$d{v$rV-_#{l~q1ldZVfa2P,B&V$Rf(IΝCki ^ǶRVv0+[hwf˛)8#WC8%LDڥ%t g} I} U9rV"џ,e1⚫=@,~iP~ EbmHPR&kgkKHrE}O@.Ň?8vqFцJR"粸*5f̴$ )xooFG4,C\@j:"0HS%R91l+ϕX pS ]!ҙh,UW[ʐIXS%Q%pA<)!z?'F{ͱy4R'XTmK94LĈ0/Ȑ+ဨāT#xq SK239Q%,dePQV2 %(>2TfvPu׶"jCD^]]#l,K*2C\ۦ' #!c’ycL fY H *<8 ,)q 3,\=?%q}wMe@Arki~ D2TrhyԘYl/_?b+Qxk^G>Ƣ Ɋp7=!%B ѤOrWE[+₳ҷ>ǫrFl+y rǞ,S:ATj"`gXO"=V-i&#A&e|e^;-DvذaN29p&CnO=c Lt[OI4Y[+9f UA-E8,,'MI`TzԄ~_ŵXo{s-#>ꆸ+bsKwe>D&-a7&!`CNc h4t2fCdHJ2HjZ˗YI ^˫b=2 i뉻N&%{nɜy9KWj eF^1D"1PlcQ6R!֯R}1tpۡL;q/}1xDU521vC.2DQDjN1d d,dD9ʕK~6VX]teĤݦcg.ɔ BYGfln'NWZ'Ll)ծZ/O'MA̘rU ([SoSnJ#aeAG:Ib[ _B|Eۀ١Vp/6.} *XFicƌYaz4PS#Dm$ q]pbǙLޗqet6ns^tMcUXxlAa'&O3:uf鱝<9긃>LYQKpmrz$nLEAA,;ynTiF `֮viJFP aII ,h!*; !Alf ڥ!vkxH+.8$e`{X\+܈ B;+Wǚǘ0~kNa8oCB-^y9^fogҵX Bv;]dQ\AÀuJNBoCK=xQ(@]1!G{ëYRV<&q\`}}V}|Ҹ ݕϾ?U=d89~D1 +) fzE(eHGIh3}q.<߂]׿WR׃0~l?}=q f3ɭSoI,uajhĈHn':(l/꯾-?ƢMԮ!: I<-%TG1Z{fDJv`5B3R܈]='&Ƶ]X1|Լ#1cbYYT7r &&**"\n4N=%mX&jƪOO.D㞚@<O<{{]c,|•K"jZ@?F-*Bg @/uY!/gM'f3+ѨR9Y*sF"K>̜GZbY4BhTCR(m"wut;Q=}y_,N؋:[b)- R0q0K9ݯǘg{72 ރeb[Xc|٥[6x)鏞- ~5Mlz%K+D\=ՖFڔ¨bPӐJ k);] c=}3)v?3?3G;|n;GfnW#=mprXU , ֎$pFvw.h޺y p#"5pyM]jfKX8"xckM''OE}3 E-/E6ok1/W-["tXxcCf4%))Mg l@#j{_Vθ鶸;EĞ{웶_1#~2.J?1}h#x1vyٱ7@q˧Fuv -' fM:jRHnhp]CLG$d) *'%Vx$JB(ü|)aJn)βsHrӞivڳmژQؼq]5)3ʲdr&)=}ZKCHeX1'q^O 0HN64{"4k87"C40 U<ʍTc>li~u ~vF)Yi 4 i%b\NK\dR?Q%29yl8h,rZƲm$CeȊ4lB2c8}̍9~󫟰EԊTi:HC/fMa3*+0\';@G!7^A )]P6 EH;,Aefp&z}*6ƅ+t@V+Җ&§D!H'pW"֘Ul9AO<Ўz!aP+F&NX~wzz$z}ƝsJ?2CtHO;2 oHj~o뺸ж)!wc.J#hݺsى:yWCF.szk@#$m P8d w"N<9ARCE` ]0 6èiB )$iB4Vܢs;0H^)Ma"S]ȬGXXZܐ;I^-jT*Lluү%eҊYo~vy܅Y4=dLSz{($\ &ƌJPQAR)f:K[I{mTNjZJۄʢ"d [įcN "9Z{C#E?{Mn݆*p$R6{on$L@ =D!H;ᘹBm SjC~rFdRzs)CEtx@ 7= 2փ8,ƍX|MGֱj&B0m>`p%q &T8yÓ̂p9Z0Ks 'e"S)dU.w;ɓգ[F$Zd yDŵi9%iEJB%STJ }N}qUxđu(u\k[2 yh؏,'x?1Bf*6!PS$Ic>׬^zv2X~e 7IOxg=RڹN 99oBtCTuW=&[V^EE˩yM$GeCyhp?ϣԅ2qq҇ -Qv\?@.LnmR\N8U" 0y& x82D'YJ_`D:MUj&_kN<8Q6i}"?x`#3ޓ* l©;}-7 MlXNdV뮻1sgNP u d%o Y̰*ӅZ(CC6H71O)>BLfMJs; 2l#T%OF¤G&.`R ;|g?].y:X6X';',&򒪉Q SOy*J* DA)!JiR #I5S=N-7A.R=lD)62Nxo4VзR*RPvVD4ay?W].l5{ C Q2VBlX K\l aj>[o P`* ׵3g@%RsUYȒËϠʔ;EgUmUN29;ݯ?tb";s|><,n8dԸ+#CTͲI;o(te͌f5p.ѿ<.!ξ4uf~O/YIyѪ ]*w18F@f1{OJW^BIX>zێ&,'6HlZ!pRo~KȔ[JC6VI'XtJrK[*߈pExyl_!dIj_:bb ?sН~ }YCql}׼w/bĸ˰,'] ]^Nuˇ]|4z=fvS=cv閛lO1-&QcR2J y(3  %KF:3N'Șv+&ʼusv|u鼵Xk]S3i5 -SQ^6By7)Lx ު@eå:T`=+qk4 ݸ{z 2[䓙WӢtv\ITv_9Gv RɣN*m/p#8\'wuy_59:f5Y9£G=qK_{_υNڄSSpܪA*6*` Ͳ<TINj !]Xɴ]#`H QMI>X&,K {֑~ yvV5ۍA%CJM,p]7] ћ[ J2ge+wݼgVmg-$6v "5hq6T-X#]bAHߑZT}q޿-<1&ty{ 'ljH;V9HQ2bNP_υJxm*yXf jϪ-*q"#$$0s[@6\\fAXtuv٘ 0&;KJ G-mR"cy[o6\M 9a~ҔA b` D2YNphQ2A&4:4 ҥjA)!Ҧ/-&O:^+ Ș&G(WQͭ|q1Sf PU1ka*jN-lmɝV&Fx\ /ƌ9z_ȝU/.EnA$o.fԶ3i/?)SOUvThv,++q2qCU^9"AP 4# 'I"BXJ.S~ 񓟱tϦ:ckboLׯsd ptUj Lq̓ rئ8mVᐘz\Xnj :/'nZŻveW8ڱ3cyIԲEٷ)jNzTCYUB*ݮ^=E>uz,QihuL|X?+U iYAr37@©""?>icĀM8"m}Nc%-IӧVxY!2{XtV OX"꾓Nd3muZ/0$J%U82dfDtC5.x\qa#G48vv1~<;c Kc<4 ~]Ȍns2mrP2Ҁ&/*Z <ě[竎Z_*FrrG#3.%sZ<ڷ+cYj96T*mV?2G GQ LcP"EHJ[osqo ©`Z7ZɬC)̯W, (4ԩi +%d Fy㩙-k*=ˣ0G7*׶ڵ48h ;hm)Q`X뵁0-,㶛㮻om7upĔB@{3v(fOnl`751rΎOщ _-8%>0f5xgÄ)~b)1s8s&NtF3S{Ͻ_1mbSNUGIEHu i_;Dx($^s}(vyQv l@]vI$D;(UDW%Ѭ3 hy4Tx (ij2%_„/X S"y H$` EK%he˗7)Vm}sh;u6n缕tgrykeѳ/ehEs@`O؇HI N{`;$RMocjtXi8Eػmr"sD601"`vԂ"΀# =vؒiaL:5<9J)UVR% c%T%%SM>U{Ia0 {A܃H 3fG>X1J)VYC\ WIb; % r׬Y7xs=!|jqB};뙷= ;D 6é-ZzYU NP2 ȟ[`idh;O\aNx(H)2!E ΂qrzȖ;r Sw- Q::yON;Pl }U͞;oGYU,O^&yw(Е[:d^UwUBi:Ig6B,!u-Ynm![6q ԲR5fLD!=MlI1K2p(Y z-ex)Q->JcA2 `XJb}?dWL-&Q,݀0rN 9SN4mڌ8e &(_R~f:277MQpΖP+V>CD%0L4RP81 JKO)\c_w` '0ٸl]%6OFWIJ{V%z/" -QY0v3nI`n%yJ,$TB⢭ްhcL7߭GlaĞY3A(l *(V~$!6JG]iڈA6U+AD0ZY6L`crdl#y@-3ۚkHܒ'0&v2c{i=|rmkȜ3"`%0`zΑs`7.TbλMC݂>,fKFZ?D3&ں[72Y2ߊJ\%Sȓ_[_Ӡc6_kԍnf*2@2) DVbCdJY$—C^ZFC6hr!V֗6P%p( Yv鬉5yʮ1bhKI,DeُchFSZr' e|Ǝɉ/mi6dX:uZߴ>60o%bT@̛w_,~.p1P7 VUu]KkeB;مӎJ qbC!cO'Hy%cJ m Wrs/֫HsZjh2Rs5?ݾ<( qnL ]I@M } 2,yxūu8Ə먄pT:(} \"|j= >WP,x阵d`j`UeE7CzXմ$8w"|Fh_i@.';N/bvDĈR9OZuv|"*zw"˖I/yyL2vGeD C4ykQd![H(> Ϲށ6ü 9aNdvHu<q^2\%~%1eY2E-?/Zzl{|VLL,[Ja.~r^ǰm*"'*yǥ߸$}~L5b[N黆 >lƲ}.M YIN NlE~PۈE+Kqqī_*o =bl̀FDr ,e&rxĥÔ\UsfpjLW+-?)GCFI! UUE  K(/2LP>BAr3fK*9@\Bg,#HL}uh,O~Yz4F/\:iAf)['K?3g^+W,ZE6f7 +.:1!ohϓ8e_`/idDZJ gL(XT6\ym m^N6 D*u n^7nWt'weַsh8®E-X܍F>}ݫ-q =ndp߮]jjzzJ/J؄;-3S{gƝ0+UAz':UȖK|W{g]eyބ$d7Dbu4#b;LkǪ*(R\Q+eR,x8e: eQ MBwoHҙlywG;u~be f28&8\5?$UԎh56c2*$u&VBQAv]YΎ\3~Ek4k*&?y @\Id 'Nrd3̨Y\v QGGLd"znZxa|yWQ,_v~g+\Rj8I@@ PK|2<:_!-;j^mRSf,Gĉn : VWvF?B$q|bRrӚ apq@ԮzZM{+U;$k PvumV7c0ق=&}`/& c_^Vf\fɯ*CZ`aݏgzPLkQ֚.H;1HpRG5DQ !qKNLTt(m*À8 B;Mt N ;Eͫ /L)=^jk(ʙXJv4~k%S8.vcei( :Ut_)J&SJ(-~DĺI߲u;u#WO.KFI7~"lExM<^+rS̼s~]G}Yk5ܴi͞-x%ơ+;ZZPa@5/֕7UOrWo:R]޼$.1"d$wխ)Aa89NpȓWRzz˹"*ԫ\,  F@rj93[Sư'RÐcԮ ! q=jH'-X|h BڜC]l`ңG7;{?^q۴ɷ:q'Cf8?4F >\&Ec `?,+pوuLƬB~TqEXͨI/:˝&}:˚sje}KD=3R #\ٚO[}9Ipt0j/Zo'n5?$P338I> szVo~Q]eY #U5H#Q)+vCȦ/i N{M6Acu#ts; )5pYLBN,+å2Ie8^3(N]UCŶd"oIv˼|a5 e;\&šnZSg]?w>H간4hXfn k%J"G iE-xe/m#ng iu\ \u'zT2ӶW7l?|;s=1zH? V˓n#(ݧɩh5%kI.^Vr6nd?_Mlef̈́ӲTεEP0'i\!+LDYUTM{+*Xd͘;ݪcMa1d -E:[WZS::#*H4kV֗[Q^] ;^1 \px.4Ĝ[gA@qz Ro]={楧fQI C\ CҰ2 )` 0*@\SS[H܌;Rgo{sJ[SyiS bJfjBcKehGMxUi&Qa۶m/Uo#B Uf2${k;tIl"w&Sc%>d#.Сnkdhc!Zqǟ>8ݷ[2˅n͓E"Y3Wr\ >c!n16I1g=4Ö.^ bB)"WA -$ b1hI$cD< [h3mdY!.{gjv|C]9~;ʌtIcpKJ%%!%4_ es:EqmG >ՌC5Ԗ5~?uw?S_$p 1 @5q 5(n {3VRr~v ~hِdN R y,}hw]: ı}N;C?w$ЇiS 3_^u؎gIo?0olP18(!*0C^PspQ@|K뜶+ذv6+txŲw Mˉ=dW]c82#bpX87E%XR&ޑÇX@^RnSRS(m@ \[e[r$N?L2Yg:N2()ɯ!ÓzivK]u8-#p0[0صp#}m_4>,ùPJNnhXjٞ !VvVOj3齠cZQ ZJ-jKh<[IJ5KԎK"-q. (E /\"_{ \e@YrfůjbW 'E`LM28TizӛM?w Kޏ~ žQ ,[hޏ즛n@E"*"V6 H/CH[ZzrlL !W4H7˺o]%Xi0ou:]4M{Ki+ukؿuˆ&!Ҧ6g0|'>хF{}{%~֊*dKu.L2g] H ,0nbi̐)AQ]QIHPY#%aNW`)؁|u6#~Op!ԤD<M߽{=lƋLRQ)wP uMlfQ^v/3NU:k% ;͡%İϱ%{-.R#E =ؑau8NWLu$;òǵB:=)%1ҥ29$r$[Y3Yݻ_2';x$-'H: l!7UWMOr'{z[TX|?cW$gL~T` k7qkJtȚ%wI 23dyK\+O t-Zyb'ybVTTܞcX۪;P%oԦH!늑T "(_wGa</Vc <Y\3=;94+:? #xgrZc [fKfFV(*WZ0-.Ѐ88:aW$#NzWX̓i[[<@! =,&w OËW1?[Y =8tJa.sLX2"=}=h 3Z҆Wbwg:J.Rf[e zEXg " %'D K)sEFgiWB{OغU,ed_`TqLR:WNXKS.ݤ~g{zVbFg8|x%-. w{GH\<߷4ٴoN;eef9m62!"O,mCYLN}4p}D%bF/-pK.(VLc:fZs՞wvĝ{%7'5V%mQW^X2.+%Wid@[R`ũ2wBES2!Ê! :;{:T|%rJ~6Smm&6>UE _aݻ|]?d&?q4]CJv&,)[@XP!'ZZZ*lD\/T,T*},<ۻoPc :-_QƍT߻ J!|.G" -fm=>8g p3uT,b]ーd(aDU1+)%,y5j#>TBp٪XF::c?Z$9/~jo.,j '<\RPN-+Q|Vr\'pAUZةx:Oy54$24&*k?nl~+\f]j1Aز THIIx=OyYW \VFVZE_Eiy iiL)kad''$'7؊+ p#&: ?6ڢxm%?n8^mNf-v(c vFV(5Ū|HZWQ'8FKc҉s8_80=͆v%Q؟hv{m 7}矴>saĩꎱx0:va Jh7H3&*X oluiCc'dRE'a;J/-Ņm#Q}߻yt9gr瑚W9 1g\Xş#¢}ǫ&#m09SMM͕0^93 v_Ys@7vQ‹"SMQm }5P`/csw~rmk҆eFg`/=nM& T| `Z-)5p64>Ǡju^pQ IENDB`purrr/man/pipe.Rd0000644000176200001440000000032013147024352013417 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{\%>\%} \alias{\%>\%} \title{Pipe operator} \usage{ lhs \%>\% rhs } \description{ Pipe operator } \keyword{internal} purrr/man/modify.Rd0000644000176200001440000001262213171451356013766 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/modify.R \name{modify} \alias{modify} \alias{modify.default} \alias{modify_if} \alias{modify_if.default} \alias{modify_at} \alias{modify_at.default} \alias{modify_depth} \alias{modify_depth.default} \alias{at_depth} \title{Modify elements selectively} \usage{ modify(.x, .f, ...) \method{modify}{default}(.x, .f, ...) modify_if(.x, .p, .f, ...) \method{modify_if}{default}(.x, .p, .f, ...) modify_at(.x, .at, .f, ...) \method{modify_at}{default}(.x, .at, .f, ...) modify_depth(.x, .depth, .f, ..., .ragged = .depth < 0) \method{modify_depth}{default}(.x, .depth, .f, ..., .ragged = .depth < 0) } \arguments{ \item{.x}{A list or atomic vector.} \item{.f}{A function, formula, or atomic vector. If a \strong{function}, it is used as is. If a \strong{formula}, e.g. \code{~ .x + 2}, it is converted to a function. There are three ways to refer to the arguments: \itemize{ \item For a single argument function, use \code{.} \item For a two argument function, use \code{.x} and \code{.y} \item For more arguments, use \code{..1}, \code{..2}, \code{..3} etc } This syntax allows you to create very compact anonymous functions. If \strong{character vector}, \strong{numeric vector}, or \strong{list}, it is converted to an extractor function. Character vectors index by name and numeric vectors index by position; use a list to index by position and name at different levels. Within a list, wrap strings in \code{\link[=get-attr]{get-attr()}} to extract named attributes. If a component is not present, the value of \code{.default} will be returned.} \item{...}{Additional arguments passed on to \code{.f}.} \item{.p}{A single predicate function, a formula describing such a predicate function, or a logical vector of the same length as \code{.x}. Alternatively, if the elements of \code{.x} are themselves lists of objects, a string indicating the name of a logical element in the inner lists. Only those elements where \code{.p} evaluates to \code{TRUE} will be modified.} \item{.at}{A character vector of names or a numeric vector of positions. Only those elements corresponding to \code{.at} will be modified.} \item{.depth}{Level of \code{.x} to map on. Use a negative value to count up from the lowest level of the list. \itemize{ \item \code{modify_depth(x, 0, fun)} is equivalent to \code{x[] <- fun(x)} \item \code{modify_depth(x, 1, fun)} is equivalent to \code{x[] <- map(x, fun)} \item \code{modify_depth(x, 2, fun)} is equivalent to \code{x[] <- map(x, ~ map(., fun))} }} \item{.ragged}{If \code{TRUE}, will apply to leaves, even if they're not at depth \code{.depth}. If \code{FALSE}, will throw an error if there are no elements at depth \code{.depth}.} } \value{ An object the same class as \code{.x} } \description{ \code{modify()} is a short-cut for \code{x[] <- map(x, .f); return(x)}. \code{modify_if()} only modifies the elements of \code{x} that satisfy a predicate and leaves the others unchanged. \code{modify_at()} only modifies elements given by names or positions. \code{modify_depth()} only modifies elements at a given level of a nested data structure. } \details{ Since the transformation can alter the structure of the input; it's your responsibility to ensure that the transformation produces a valid output. For example, if you're modifying a data frame, \code{.f} must preserve the length of the input. } \section{Genericity}{ All these functions are S3 generic. However, the default method is sufficient in many cases. It should be suitable for any data type that implements the subset-assignment method \code{[<-}. In some cases it may make sense to provide a custom implementation with a method suited to your S3 class. For example, a \code{grouped_df} method might take into account the grouped nature of a data frame. } \examples{ # Convert factors to characters iris \%>\% modify_if(is.factor, as.character) \%>\% str() # Specify which columns to map with a numeric vector of positions: mtcars \%>\% modify_at(c(1, 4, 5), as.character) \%>\% str() # Or with a vector of names: mtcars \%>\% modify_at(c("cyl", "am"), as.character) \%>\% str() list(x = rbernoulli(100), y = 1:100) \%>\% transpose() \%>\% modify_if("x", ~ update_list(., y = ~ y * 100)) \%>\% transpose() \%>\% simplify_all() # Modify at specified depth --------------------------- l1 <- list( obj1 = list( prop1 = list(param1 = 1:2, param2 = 3:4), prop2 = list(param1 = 5:6, param2 = 7:8) ), obj2 = list( prop1 = list(param1 = 9:10, param2 = 11:12), prop2 = list(param1 = 12:14, param2 = 15:17) ) ) # In the above list, "obj" is level 1, "prop" is level 2 and "param" # is level 3. To apply sum() on all params, we map it at depth 3: l1 \%>\% modify_depth(3, sum) \%>\% str() # modify() lets us pluck the elements prop1/param2 in obj1 and obj2: l1 \%>\% modify(c("prop1", "param2")) \%>\% str() # But what if we want to pluck all param2 elements? Then we need to # act at a lower level: l1 \%>\% modify_depth(2, "param2") \%>\% str() # modify_depth() can be with other purrr functions to make them operate at # a lower level. Here we ask pmap() to map paste() simultaneously over all # elements of the objects at the second level. paste() is effectively # mapped at level 3. l1 \%>\% modify_depth(2, ~ pmap(., paste, sep = " / ")) \%>\% str() } \seealso{ Other map variants: \code{\link{imap}}, \code{\link{invoke}}, \code{\link{lmap}}, \code{\link{map2}}, \code{\link{map}} } purrr/man/compose.Rd0000644000176200001440000000067113124210513014127 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/compose.R \name{compose} \alias{compose} \title{Compose multiple functions} \usage{ compose(...) } \arguments{ \item{...}{n functions to apply in order from right to left.} } \value{ A function } \description{ Compose multiple functions } \examples{ not_null <- compose(`!`, is.null) not_null(4) not_null(NULL) add1 <- function(x) x + 1 compose(add1, add1)(8) } purrr/man/list_modify.Rd0000644000176200001440000000321213137651733015017 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/list-modify.R \name{list_modify} \alias{list_modify} \alias{list_merge} \alias{update_list} \title{Modify a list} \usage{ list_modify(.x, ...) list_merge(.x, ...) } \arguments{ \item{.x}{List to modify.} \item{...}{New values of a list. Use \code{NULL} to remove values. Use a formula to evaluate in the context of the list values. These dots have \link[rlang:dots_list]{splicing semantics}.} } \description{ \code{list_modify()} and \code{list_merge()} recursively combine two lists, matching elements either by name or position. If an sub-element is present in both lists \code{list_modify()} takes the value from \code{y}, and \code{list_merge()} concatenates the values together. \code{update_list()} handles formulas and quosures that can refer to values existing within the input list. Note that this function might be deprecated in the future in favour of a \code{dplyr::mutate()} method for lists. } \examples{ x <- list(x = 1:10, y = 4, z = list(a = 1, b = 2)) str(x) # Update values str(list_modify(x, a = 1)) # Replace values str(list_modify(x, z = 5)) str(list_modify(x, z = list(a = 1:5))) # Remove values str(list_modify(x, z = NULL)) # Combine values str(list_merge(x, x = 11, z = list(a = 2:5, c = 3))) # All these functions take dots with splicing. Use !!! or UQS() to # splice a list of arguments: l <- list(new = 1, y = NULL, z = 5) str(list_modify(x, !!! l)) # In update_list() you can also use quosures and formulas to # compute new values. This function is likely to be deprecated in # the future update_list(x, z1 = ~z[[1]]) update_list(x, z = rlang::quo(x + y)) } purrr/man/negate.Rd0000644000176200001440000000217113124210513013722 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/negate.R \name{negate} \alias{negate} \title{Negate a predicate function.} \usage{ negate(.p, .default = FALSE) } \arguments{ \item{.p}{A single predicate function, a formula describing such a predicate function, or a logical vector of the same length as \code{.x}. Alternatively, if the elements of \code{.x} are themselves lists of objects, a string indicating the name of a logical element in the inner lists. Only those elements where \code{.p} evaluates to \code{TRUE} will be modified.} \item{.default}{Optional additional argument for extractor functions (i.e. when \code{.f} is character, integer, or list). Returned when value is absent (does not exist) or empty (has length 0). \code{.null} is deprecated; please use \code{.default} instead.} } \value{ A new predicate function. } \description{ Negate a predicate function. } \examples{ negate("x") negate(is.null) negate(~ .x > 0) x <- transpose(list(x = 1:10, y = rbernoulli(10))) x \%>\% keep("y") \%>\% length() x \%>\% keep(negate("y")) \%>\% length() # Same as x \%>\% discard("y") \%>\% length() } purrr/man/rdunif.Rd0000644000176200001440000000071013124210513013743 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{rdunif} \alias{rdunif} \title{Generate random sample from a discrete uniform distribution} \usage{ rdunif(n, b, a = 1) } \arguments{ \item{n}{Number of samples to draw.} \item{a, b}{Range of the distribution (inclusive).} } \description{ Generate random sample from a discrete uniform distribution } \examples{ table(rdunif(1e3, 10)) table(rdunif(1e3, 10, -5)) } purrr/man/rerun.Rd0000644000176200001440000000147713124210513013622 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/rerun.R \name{rerun} \alias{rerun} \title{Re-run expressions multiple times.} \usage{ rerun(.n, ...) } \arguments{ \item{.n}{Number of times to run expressions} \item{...}{Expressions to re-run.} } \value{ A list of length \code{.n}. Each element of \code{...} will be re-run once for each \code{.n}. It There is one special case: if there's a single unnamed input, the second level list will be dropped. In this case, \code{rerun(n, x)} behaves like \code{replicate(n, x, simplify = FALSE)}. } \description{ This is a convenient way of generating sample data. It works similarly to \code{\link{replicate}(..., simplify = FALSE)}. } \examples{ 10 \%>\% rerun(rnorm(5)) 10 \%>\% rerun(x = rnorm(5), y = rnorm(5)) \%>\% map_dbl(~ cor(.x$x, .x$y)) } purrr/man/keep.Rd0000644000176200001440000000300313124210513013376 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/keep.R \name{keep} \alias{keep} \alias{discard} \alias{compact} \title{Keep or discard elements using a predicate function.} \usage{ keep(.x, .p, ...) discard(.x, .p, ...) compact(.x, .p = identity) } \arguments{ \item{.x}{A list or vector.} \item{.p}{A single predicate function, a formula describing such a predicate function, or a logical vector of the same length as \code{.x}. Alternatively, if the elements of \code{.x} are themselves lists of objects, a string indicating the name of a logical element in the inner lists. Only those elements where \code{.p} evaluates to \code{TRUE} will be modified.} \item{...}{Additional arguments passed on to \code{.p}.} } \description{ \code{keep} and \code{discard} are opposites. \code{compact} is a handy wrapper that removes all elements that are \code{NULL}. } \details{ These are usually called \code{select} or \code{filter} and \code{reject} or \code{drop}, but those names are already taken. \code{keep} is similar to \code{\link[=Filter]{Filter()}} but the argument order is more convenient, and the evaluation of \code{.f} is stricter. } \examples{ rep(10, 10) \%>\% map(sample, 5) \%>\% keep(function(x) mean(x) > 6) # Or use a formula rep(10, 10) \%>\% map(sample, 5) \%>\% keep(~ mean(.x) > 6) # Using a string instead of a function will select all list elements # where that subelement is TRUE x <- rerun(5, a = rbernoulli(1), b = sample(10)) x x \%>\% keep("a") x \%>\% discard("a") } purrr/man/has_element.Rd0000644000176200001440000000064013102346452014752 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/find-position.R \name{has_element} \alias{has_element} \title{Does a list contain an object?} \usage{ has_element(.x, .y) } \arguments{ \item{.x}{A list or atomic vector.} \item{.y}{Object to test for} } \description{ Does a list contain an object? } \examples{ x <- list(1:10, 5, 9.9) x \%>\% has_element(1:10) x \%>\% has_element(3) } purrr/man/map.Rd0000644000176200001440000001207213171451433013247 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/map.R \name{map} \alias{map} \alias{map_if} \alias{map_at} \alias{map_lgl} \alias{map_chr} \alias{map_int} \alias{map_dbl} \alias{map_dfr} \alias{map_df} \alias{map_dfc} \alias{walk} \title{Apply a function to each element of a vector} \usage{ map(.x, .f, ...) map_if(.x, .p, .f, ...) map_at(.x, .at, .f, ...) map_lgl(.x, .f, ...) map_chr(.x, .f, ...) map_int(.x, .f, ...) map_dbl(.x, .f, ...) map_dfr(.x, .f, ..., .id = NULL) map_dfc(.x, .f, ...) walk(.x, .f, ...) } \arguments{ \item{.x}{A list or atomic vector.} \item{.f}{A function, formula, or atomic vector. If a \strong{function}, it is used as is. If a \strong{formula}, e.g. \code{~ .x + 2}, it is converted to a function. There are three ways to refer to the arguments: \itemize{ \item For a single argument function, use \code{.} \item For a two argument function, use \code{.x} and \code{.y} \item For more arguments, use \code{..1}, \code{..2}, \code{..3} etc } This syntax allows you to create very compact anonymous functions. If \strong{character vector}, \strong{numeric vector}, or \strong{list}, it is converted to an extractor function. Character vectors index by name and numeric vectors index by position; use a list to index by position and name at different levels. Within a list, wrap strings in \code{\link[=get-attr]{get-attr()}} to extract named attributes. If a component is not present, the value of \code{.default} will be returned.} \item{...}{Additional arguments passed on to \code{.f}.} \item{.p}{A single predicate function, a formula describing such a predicate function, or a logical vector of the same length as \code{.x}. Alternatively, if the elements of \code{.x} are themselves lists of objects, a string indicating the name of a logical element in the inner lists. Only those elements where \code{.p} evaluates to \code{TRUE} will be modified.} \item{.at}{A character vector of names or a numeric vector of positions. Only those elements corresponding to \code{.at} will be modified.} \item{.id}{If not \code{NULL} a variable with this name will be created giving either the name or the index of the data frame.} } \value{ All functions return a vector the same length as \code{.x}. \code{map()} returns a list, \code{map_lgl()} a logical vector, \code{map_int()} an integer vector, \code{map_dbl()} a double vector, and \code{map_chr()} a character vector. The output of \code{.f} will be automatically typed upwards, e.g. logical -> integer -> double -> character. \code{walk()} returns the input \code{.x} (invisibly). This makes it easy to use in pipe. } \description{ The map functions transform their input by applying a function to each element and returning a vector the same length as the input. \itemize{ \item \code{map()}, \code{map_if()} and \code{map_at()} always return a list. See the \code{\link[=modify]{modify()}} family for versions that return an object of the same type as the input. The \code{_if} and \code{_at} variants take a predicate function \code{.p} that determines which elements of \code{.x} are transformed with \code{.f}. \item \code{map_lgl()}, \code{map_int()}, \code{map_dbl()} and \code{map_chr()} return vectors of the corresponding type (or die trying). \item \code{map_dfr()} and \code{map_dfc()} return data frames created by row-binding and column-binding respectively. They require dplyr to be installed. \item \code{walk()} calls \code{.f} for its side-effect and returns the input \code{.x}. } } \examples{ 1:10 \%>\% map(rnorm, n = 10) \%>\% map_dbl(mean) # Or use an anonymous function 1:10 \%>\% map(function(x) rnorm(10, x)) # Or a formula 1:10 \%>\% map(~ rnorm(10, .x)) # Extract by name or position # .default specifies value for elements that are missing or NULL l1 <- list(list(a = 1L), list(a = NULL, b = 2L), list(b = 3L)) l1 \%>\% map("a", .default = "???") l1 \%>\% map_int("b", .default = NA) l1 \%>\% map_int(2, .default = NA) # Supply multiple values to index deeply into a list l2 <- list( list(num = 1:3, letters[1:3]), list(num = 101:103, letters[4:6]), list() ) l2 \%>\% map(c(2, 2)) # Use a list to build an extractor that mixes numeric indices and names, # and .default to provide a default value if the element does not exist l2 \%>\% map(list("num", 3)) l2 \%>\% map_int(list("num", 3), .default = NA) # A more realistic example: split a data frame into pieces, fit a # model to each piece, summarise and extract R^2 mtcars \%>\% split(.$cyl) \%>\% map(~ lm(mpg ~ wt, data = .x)) \%>\% map(summary) \%>\% map_dbl("r.squared") # Use map_lgl(), map_dbl(), etc to reduce to a vector. # * list mtcars \%>\% map(sum) # * vector mtcars \%>\% map_dbl(sum) # If each element of the output is a data frame, use # map_dfr to row-bind them together: mtcars \%>\% split(.$cyl) \%>\% map(~ lm(mpg ~ wt, data = .x)) \%>\% map_dfr(~ as.data.frame(t(as.matrix(coef(.))))) # (if you also want to preserve the variable names see # the broom package) } \seealso{ Other map variants: \code{\link{imap}}, \code{\link{invoke}}, \code{\link{lmap}}, \code{\link{map2}}, \code{\link{modify}} } purrr/man/splice.Rd0000644000176200001440000000122413124210513013734 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/splice.R \name{splice} \alias{splice} \title{Splice objects and lists of objects into a list} \usage{ splice(...) } \arguments{ \item{...}{Objects to concatenate.} } \value{ A list. } \description{ This splices all arguments into a list. Non-list objects and lists with a S3 class are encapsulated in a list before concatenation. } \examples{ inputs <- list(arg1 = "a", arg2 = "b") # splice() concatenates the elements of inputs with arg3 splice(inputs, arg3 = c("c1", "c2")) \%>\% str() list(inputs, arg3 = c("c1", "c2")) \%>\% str() c(inputs, arg3 = c("c1", "c2")) \%>\% str() } purrr/man/detect.Rd0000644000176200001440000000501013171451356013740 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/find-position.R \name{detect} \alias{detect} \alias{detect_index} \title{Find the value or position of the first match.} \usage{ detect(.x, .f, ..., .right = FALSE, .p) detect_index(.x, .f, ..., .right = FALSE, .p) } \arguments{ \item{.x}{A list or atomic vector.} \item{.f}{A function, formula, or atomic vector. If a \strong{function}, it is used as is. If a \strong{formula}, e.g. \code{~ .x + 2}, it is converted to a function. There are three ways to refer to the arguments: \itemize{ \item For a single argument function, use \code{.} \item For a two argument function, use \code{.x} and \code{.y} \item For more arguments, use \code{..1}, \code{..2}, \code{..3} etc } This syntax allows you to create very compact anonymous functions. If \strong{character vector}, \strong{numeric vector}, or \strong{list}, it is converted to an extractor function. Character vectors index by name and numeric vectors index by position; use a list to index by position and name at different levels. Within a list, wrap strings in \code{\link[=get-attr]{get-attr()}} to extract named attributes. If a component is not present, the value of \code{.default} will be returned.} \item{...}{Additional arguments passed on to \code{.f}.} \item{.right}{If \code{FALSE}, the default, starts at the beginning of the vector and move towards the end; if \code{TRUE}, starts at the end of the vector and moves towards the beginning.} \item{.p}{A single predicate function, a formula describing such a predicate function, or a logical vector of the same length as \code{.x}. Alternatively, if the elements of \code{.x} are themselves lists of objects, a string indicating the name of a logical element in the inner lists. Only those elements where \code{.p} evaluates to \code{TRUE} will be modified.} } \value{ \code{detect} the value of the first item that matches the predicate; \code{detect_index} the position of the matching item. If not found, \code{detect} returns \code{NULL} and \code{detect_index} returns 0. } \description{ Find the value or position of the first match. } \examples{ is_even <- function(x) x \%\% 2 == 0 3:10 \%>\% detect(is_even) 3:10 \%>\% detect_index(is_even) 3:10 \%>\% detect(is_even, .right = TRUE) 3:10 \%>\% detect_index(is_even, .right = TRUE) # Since `.f` is passed to as_mapper(), you can supply a # lambda-formula or a pluck object: x <- list( list(1, foo = FALSE), list(2, foo = TRUE), list(3, foo = TRUE) ) detect(x, "foo") detect_index(x, "foo") } purrr/man/set_names.Rd0000644000176200001440000000247513147024405014454 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/set_names.R \docType{import} \name{set_names} \alias{set_names} \title{Set names in a vector} \usage{ set_names(x, nm = x, ...) } \arguments{ \item{x}{Vector to name} \item{nm, ...}{Vector of names, the same length as \code{x}. You can specify names in three ways: \itemize{ \item If you do nothing, \code{x} will be named with itself \item You can supply either a character vector to \code{nm} or individual strings in to `...`` \item If \code{x} already has names, you can provide a function or formula to transform the existing names. }} } \value{ \code{.x} with the names attribute set. } \details{ This is a snake case wrapper for \code{\link[stats:setNames]{stats::setNames()}}, with tweaked defaults, and stricter argument checking. } \examples{ set_names(1:4, c("a", "b", "c", "d")) set_names(1:4, letters[1:4]) set_names(1:4, "a", "b", "c", "d") # If the second argument is ommitted a vector is named with itself set_names(letters[1:5]) # Alternatively you can supply a function set_names(1:10, ~ letters[seq_along(.)]) set_names(head(mtcars), toupper) } \keyword{internal} \description{ These objects are imported from other packages. Follow the links below to see their documentation. \describe{ \item{rlang}{\code{\link[rlang]{set_names}}} }} purrr/man/prepend.Rd0000644000176200001440000000140513124210513014113 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/prepend.R \name{prepend} \alias{prepend} \title{Prepend a vector} \usage{ prepend(x, values, before = 1) } \arguments{ \item{x}{the vector to be modified.} \item{values}{to be included in the modified vector.} \item{before}{a subscript, before which the values are to be appended.} } \value{ A merged vector. } \description{ This is a companion to \code{\link[=append]{append()}} to help merging two lists or atomic vectors. \code{prepend()} is a clearer semantic signal than \code{c()} that a vector is to be merged at the beginning of another, especially in a pipe chain. } \examples{ x <- as.list(1:3) x \%>\% append("a") x \%>\% prepend("a") x \%>\% prepend(list("a", "b"), before = 3) } purrr/man/safely.Rd0000644000176200001440000000560213171451356013762 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/output.R \name{safely} \alias{safely} \alias{quietly} \alias{possibly} \alias{auto_browse} \title{Capture side effects.} \usage{ safely(.f, otherwise = NULL, quiet = TRUE) quietly(.f) possibly(.f, otherwise, quiet = TRUE) auto_browse(.f) } \arguments{ \item{.f}{A function, formula, or atomic vector. If a \strong{function}, it is used as is. If a \strong{formula}, e.g. \code{~ .x + 2}, it is converted to a function. There are three ways to refer to the arguments: \itemize{ \item For a single argument function, use \code{.} \item For a two argument function, use \code{.x} and \code{.y} \item For more arguments, use \code{..1}, \code{..2}, \code{..3} etc } This syntax allows you to create very compact anonymous functions. If \strong{character vector}, \strong{numeric vector}, or \strong{list}, it is converted to an extractor function. Character vectors index by name and numeric vectors index by position; use a list to index by position and name at different levels. Within a list, wrap strings in \code{\link[=get-attr]{get-attr()}} to extract named attributes. If a component is not present, the value of \code{.default} will be returned.} \item{otherwise}{Default value to use when an error occurs.} \item{quiet}{Hide errors (\code{TRUE}, the default), or display them as they occur?} } \value{ \code{safely}: wrapped function instead returns a list with components \code{result} and \code{error}. One value is always \code{NULL}. \code{quietly}: wrapped function instead returns a list with components \code{result}, \code{output}, \code{messages} and \code{warnings}. \code{possibly}: wrapped function uses a default value (\code{otherwise}) whenever an error occurs. } \description{ These functions wrap functions so that instead of generating side effects through printed output, messages, warnings, and errors, they return enhanced output. They are all adverbs because they modify the action of a verb (a function). } \examples{ safe_log <- safely(log) safe_log(10) safe_log("a") list("a", 10, 100) \%>\% map(safe_log) \%>\% transpose() # This is a bit easier to work with if you supply a default value # of the same type and use the simplify argument to transpose(): safe_log <- safely(log, otherwise = NA_real_) list("a", 10, 100) \%>\% map(safe_log) \%>\% transpose() \%>\% simplify_all() # To replace errors with a default value, use possibly(). list("a", 10, 100) \%>\% map_dbl(possibly(log, NA_real_)) # For interactive usage, auto_browse() is useful because it automatically # starts a browser() in the right place. f <- function(x) { y <- 20 if (x > 5) { stop("!") } else { x } } if (interactive()) { map(1:6, auto_browse(f)) } # It doesn't make sense to use auto_browse with primitive functions, # because they are implemented in C so there's no useful environment # for you to interact with. } purrr/man/null-default.Rd0000644000176200001440000000076713075737303015104 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{null-default} \alias{null-default} \alias{\%||\%} \title{Default value for \code{NULL}.} \usage{ x \%||\% y } \arguments{ \item{x, y}{If \code{x} is NULL, will return \code{y}; otherwise returns \code{x}.} } \description{ This infix function makes it easy to replace \code{NULL}s with a default value. It's inspired by the way that Ruby's or operation (\code{||}) works. } \examples{ 1 \%||\% 2 NULL \%||\% 2 } purrr/man/partial.Rd0000644000176200001440000000440113137651733014132 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/partial.R \name{partial} \alias{partial} \title{Partial apply a function, filling in some arguments.} \usage{ partial(...f, ..., .env = parent.frame(), .lazy = TRUE, .first = TRUE) } \arguments{ \item{...f}{a function. For the output source to read well, this should be a named function.} \item{...}{named arguments to \code{...f} that should be partially applied.} \item{.env}{the environment of the created function. Defaults to \code{\link[=parent.frame]{parent.frame()}} and you should rarely need to modify this.} \item{.lazy}{If \code{TRUE} arguments evaluated lazily, if \code{FALSE}, evaluated when \code{partial} is called.} \item{.first}{If \code{TRUE}, the partialized arguments are placed to the front of the function signature. If \code{FALSE}, they are moved to the back. Only useful to control position matching of arguments when the partialized arguments are not named.} } \description{ Partial function application allows you to modify a function by pre-filling some of the arguments. It is particularly useful in conjunction with functionals and other function operators. } \section{Design choices}{ There are many ways to implement partial function application in R. (see e.g. \code{dots} in \url{https://github.com/crowding/ptools} for another approach.) This implementation is based on creating functions that are as similar as possible to the anonymous functions that you'd create by hand, if you weren't using \code{partial}. } \examples{ # Partial is designed to replace the use of anonymous functions for # filling in function arguments. Instead of: compact1 <- function(x) discard(x, is.null) # we can write: compact2 <- partial(discard, .p = is.null) # and the generated source code is very similar to what we made by hand compact1 compact2 # Note that the evaluation occurs "lazily" so that arguments will be # repeatedly evaluated f <- partial(runif, n = rpois(1, 5)) f f() f() # You can override this by saying .lazy = FALSE f <- partial(runif, n = rpois(1, 5), .lazy = FALSE) f f() f() # This also means that partial works fine with functions that do # non-standard evaluation my_long_variable <- 1:10 plot2 <- partial(plot, my_long_variable) plot2() plot2(runif(10), type = "l") } purrr/man/transpose.Rd0000644000176200001440000000376513124211161014507 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/transpose.R \name{transpose} \alias{transpose} \title{Transpose a list.} \usage{ transpose(.l, .names = NULL) } \arguments{ \item{.l}{A list of vectors to zip. The first element is used as the template; you'll get a warning if a sub-list is not the same length as the first element.} \item{.names}{For efficiency, \code{transpose()} usually inspects the first component of \code{.l} to determine the structure. Use \code{.names} if you want to override this default.} } \value{ A list with indexing transposed compared to \code{.l}. } \description{ Transpose turns a list-of-lists "inside-out"; it turns a pair of lists into a list of pairs, or a list of pairs into pair of lists. For example, if you had a list of length n where each component had values \code{a} and \code{b}, \code{transpose()} would make a list with elements \code{a} and \code{b} that contained lists of length n. It's called transpose because \code{x[[1]][[2]]} is equivalent to \code{transpose(x)[[2]][[1]]}. } \details{ Note that \code{transpose()} is its own inverse, much like the transpose operation on a matrix. You can get back the original input by transposing it twice. } \examples{ x <- rerun(5, x = runif(1), y = runif(5)) x \%>\% str() x \%>\% transpose() \%>\% str() # Back to where we started x \%>\% transpose() \%>\% transpose() \%>\% str() # transpose() is useful in conjunction with safely() & quietly() x <- list("a", 1, 2) y <- x \%>\% map(safely(log)) y \%>\% str() y \%>\% transpose() \%>\% str() # Use simplify_all() to reduce to atomic vectors where possible x <- list(list(a = 1, b = 2), list(a = 3, b = 4), list(a = 5, b = 6)) x \%>\% transpose() x \%>\% transpose() \%>\% simplify_all() # Provide explicit component names to prevent loss of those that don't # appear in first component ll <- list( list(x = 1, y = "one"), list(z = "deux", x = 2) ) ll \%>\% transpose() nms <- ll \%>\% map(names) \%>\% reduce(union) ll \%>\% transpose(.names = nms) } purrr/man/along.Rd0000644000176200001440000000117113171415127013570 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/along.R \name{along} \alias{along} \alias{list_along} \alias{rep_along} \title{Helper to create vectors with matching length.} \usage{ list_along(x) rep_along(x, y) } \arguments{ \item{x}{A vector.} \item{y}{Values to repeat.} } \value{ A vector of the same length as \code{x}. } \description{ These functions take the idea of \code{\link[=seq_along]{seq_along()}} and generalise it to creating lists (\code{list_along}) and repeating values (\code{rep_along}). } \examples{ x <- 1:5 rep_along(x, 1:2) rep_along(x, 1) list_along(x) } \keyword{internal} purrr/man/cross.Rd0000644000176200001440000000744313124211161013617 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cross.R \name{cross} \alias{cross} \alias{cross2} \alias{cross3} \alias{cross_df} \alias{cross_n} \alias{cross_d} \title{Produce all combinations of list elements} \usage{ cross(.l, .filter = NULL) cross2(.x, .y, .filter = NULL) cross3(.x, .y, .z, .filter = NULL) cross_df(.l, .filter = NULL) } \arguments{ \item{.l}{A list of lists or atomic vectors. Alternatively, a data frame. \code{cross_df()} requires all elements to be named.} \item{.filter}{A predicate function that takes the same number of arguments as the number of variables to be combined.} \item{.x, .y, .z}{Lists or atomic vectors.} } \value{ \code{cross2()}, \code{cross3()} and \code{cross()} always return a list. \code{cross_df()} always returns a data frame. \code{cross()} returns a list where each element is one combination so that the list can be directly mapped over. \code{cross_df()} returns a data frame where each row is one combination. } \description{ \code{cross2()} returns the product set of the elements of \code{.x} and \code{.y}. \code{cross3()} takes an additional \code{.z} argument. \code{cross()} takes a list \code{.l} and returns the cartesian product of all its elements in a list, with one combination by element. \code{cross_df()} is like \code{cross()} but returns a data frame, with one combination by row. } \details{ \code{cross()}, \code{cross2()} and \code{cross3()} return the cartesian product is returned in wide format. This makes it more amenable to mapping operations. \code{cross_df()} returns the output in long format just as \code{expand.grid()} does. This is adapted to rowwise operations. When the number of combinations is large and the individual elements are heavy memory-wise, it is often useful to filter unwanted combinations on the fly with \code{.filter}. It must be a predicate function that takes the same number of arguments as the number of crossed objects (2 for \code{cross2()}, 3 for \code{cross3()}, \code{length(.l)} for \code{cross()}) and returns \code{TRUE} or \code{FALSE}. The combinations where the predicate function returns \code{TRUE} will be removed from the result. } \examples{ # We build all combinations of names, greetings and separators from our # list of data and pass each one to paste() data <- list( id = c("John", "Jane"), greeting = c("Hello.", "Bonjour."), sep = c("! ", "... ") ) data \%>\% cross() \%>\% map(lift(paste)) # cross() returns the combinations in long format: many elements, # each representing one combination. With cross_df() we'll get a # data frame in long format: crossing three objects produces a data # frame of three columns with each row being a particular # combination. This is the same format that expand.grid() returns. args <- data \%>\% cross_df() # In case you need a list in long format (and not a data frame) # just run as.list() after cross_df() args \%>\% as.list() # This format is often less pratical for functional programming # because applying a function to the combinations requires a loop out <- vector("list", length = nrow(args)) for (i in seq_along(out)) out[[i]] <- map(args, i) \%>\% invoke(paste, .) out # It's easier to transpose and then use invoke_map() args \%>\% transpose() \%>\% map_chr(~ invoke(paste, .)) # Unwanted combinations can be filtered out with a predicate function filter <- function(x, y) x >= y cross2(1:5, 1:5, .filter = filter) \%>\% str() # To give names to the components of the combinations, we map # setNames() on the product: seq_len(3) \%>\% cross2(., ., .filter = `==`) \%>\% map(setNames, c("x", "y")) # Alternatively we can encapsulate the arguments in a named list # before crossing to get named components: seq_len(3) \%>\% list(x = ., y = .) \%>\% cross(.filter = `==`) } \seealso{ \code{\link[=expand.grid]{expand.grid()}} } purrr/man/invoke.Rd0000644000176200001440000000637013147024405013767 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/invoke.R \name{invoke} \alias{invoke} \alias{invoke_map} \alias{invoke_map_lgl} \alias{invoke_map_int} \alias{invoke_map_dbl} \alias{invoke_map_chr} \alias{invoke_map_dfr} \alias{invoke_map_dfc} \alias{invoke_map_df} \alias{map_call} \title{Invoke functions.} \usage{ invoke(.f, .x = NULL, ..., .env = NULL) invoke_map(.f, .x = list(NULL), ..., .env = NULL) invoke_map_lgl(.f, .x = list(NULL), ..., .env = NULL) invoke_map_int(.f, .x = list(NULL), ..., .env = NULL) invoke_map_dbl(.f, .x = list(NULL), ..., .env = NULL) invoke_map_chr(.f, .x = list(NULL), ..., .env = NULL) invoke_map_dfr(.f, .x = list(NULL), ..., .env = NULL) invoke_map_dfc(.f, .x = list(NULL), ..., .env = NULL) } \arguments{ \item{.f}{For \code{invoke}, a function; for \code{invoke_map} a list of functions.} \item{.x}{For \code{invoke}, an argument-list; for \code{invoke_map} a list of argument-lists the same length as \code{.f} (or length 1). The default argument, \code{list(NULL)}, will be recycled to the same length as \code{.f}, and will call each function with no arguments (apart from any supplied in \code{...}.} \item{...}{Additional arguments passed to each function.} \item{.env}{Environment in which \code{\link[=do.call]{do.call()}} should evaluate a constructed expression. This only matters if you pass as \code{.f} the name of a function rather than its value, or as \code{.x} symbols of objects rather than their values.} } \description{ This pair of functions make it easier to combine a function and list of parameters to get a result. \code{invoke} is a wrapper around \code{do.call} that makes it easy to use in a pipe. \code{invoke_map} makes it easier to call lists of functions with lists of parameters. } \examples{ # Invoke a function with a list of arguments invoke(runif, list(n = 10)) # Invoke a function with named arguments invoke(runif, n = 10) # Combine the two: invoke(paste, list("01a", "01b"), sep = "-") # That's more natural as part of a pipeline: list("01a", "01b") \%>\% invoke(paste, ., sep = "-") # Invoke a list of functions, each with different arguments invoke_map(list(runif, rnorm), list(list(n = 10), list(n = 5))) # Or with the same inputs: invoke_map(list(runif, rnorm), list(list(n = 5))) invoke_map(list(runif, rnorm), n = 5) # Or the same function with different inputs: invoke_map("runif", list(list(n = 5), list(n = 10))) # Or as a pipeline list(m1 = mean, m2 = median) \%>\% invoke_map(x = rcauchy(100)) list(m1 = mean, m2 = median) \%>\% invoke_map_dbl(x = rcauchy(100)) # Note that you can also match by position by explicitly omitting `.x`. # This can be useful when the argument names of the functions are not # identical list(m1 = mean, m2 = median) \%>\% invoke_map(, rcauchy(100)) # If you have pairs of function name and arguments, it's natural # to store them in a data frame. Here we use a tibble because # it has better support for list-columns df <- tibble::tibble( f = c("runif", "rpois", "rnorm"), params = list( list(n = 10), list(n = 5, lambda = 10), list(n = 10, mean = -3, sd = 10) ) ) df invoke_map(df$f, df$params) } \seealso{ Other map variants: \code{\link{imap}}, \code{\link{lmap}}, \code{\link{map2}}, \code{\link{map}}, \code{\link{modify}} } purrr/man/flatten.Rd0000644000176200001440000000311213124220066014114 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/flatten.R \name{flatten} \alias{flatten} \alias{flatten_lgl} \alias{flatten_int} \alias{flatten_dbl} \alias{flatten_chr} \alias{flatten_dfr} \alias{flatten_dfc} \alias{flatten_df} \title{Flatten a list of lists into a simple vector.} \usage{ flatten(.x) flatten_lgl(.x) flatten_int(.x) flatten_dbl(.x) flatten_chr(.x) flatten_dfr(.x, .id = NULL) flatten_dfc(.x) } \arguments{ \item{.x}{A list of flatten. The contents of the list can be anything for \code{flatten} (as a list is returned), but the contents must match the type for the other functions.} \item{.id}{If not \code{NULL} a variable with this name will be created giving either the name or the index of the data frame.} } \value{ \code{flatten()} returns a list, \code{flatten_lgl()} a logical vector, \code{flatten_int()} an integer vector, \code{flatten_dbl()} a double vector, and \code{flatten_chr()} a character vector. \code{flatten_dfr()} and \code{flatten_dfc()} return data frames created by row-binding and column-binding respectively. They require dplyr to be installed. } \description{ These functions remove a level hierarchy from a list. They are similar to \code{\link[=unlist]{unlist()}}, only ever remove a single layer of hierarchy, and are type-stable so you always know what the type of the output is. } \examples{ x <- rerun(2, sample(4)) x x \%>\% flatten() x \%>\% flatten_int() # You can use flatten in conjunction with map x \%>\% map(1L) \%>\% flatten_int() # But it's more efficient to use the typed map instead. x \%>\% map_int(1L) } purrr/man/lmap.Rd0000644000176200001440000000735413171415127013432 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/lmap.R \name{lmap} \alias{lmap} \alias{lmap_if} \alias{lmap_at} \title{Apply a function to list-elements of a list} \usage{ lmap(.x, .f, ...) lmap_if(.x, .p, .f, ...) lmap_at(.x, .at, .f, ...) } \arguments{ \item{.x}{A list or data frame.} \item{.f}{A function that takes and returns a list or data frame.} \item{...}{Additional arguments passed on to \code{.f}.} \item{.p}{A single predicate function, a formula describing such a predicate function, or a logical vector of the same length as \code{.x}. Alternatively, if the elements of \code{.x} are themselves lists of objects, a string indicating the name of a logical element in the inner lists. Only those elements where \code{.p} evaluates to \code{TRUE} will be modified.} \item{.at}{A character vector of names or a numeric vector of positions. Only those elements corresponding to \code{.at} will be modified.} } \value{ If \code{.x} is a list, a list. If \code{.x} is a data frame, a data frame. } \description{ \code{lmap()}, \code{lmap_at()} and \code{lmap_if()} are similar to \code{map()}, \code{map_at()} and \code{map_if()}, with the difference that they operate exclusively on functions that take \emph{and} return a list (or data frame). Thus, instead of mapping the elements of a list (as in \code{.x[[i]]}), they apply a function \code{.f} to each subset of size 1 of that list (as in \code{.x[i]}). We call those those elements \code{list-elements}). } \details{ Mapping the list-elements \code{.x[i]} has several advantages. It makes it possible to work with functions that exclusively take a list or data frame. It enables \code{.f} to access the attributes of the encapsulating list, like the name of the components it receives. It also enables \code{.f} to return a larger list than the list-element of size 1 it got as input. Conversely, \code{.f} can also return empty lists. In these cases, the output list is reshaped with a different size than the input list \code{.x}. } \examples{ # Let's write a function that returns a larger list or an empty list # depending on some condition. This function also uses the names # metadata available in the attributes of the list-element maybe_rep <- function(x) { n <- rpois(1, 2) out <- rep_len(x, n) if (length(out) > 0) { names(out) <- paste0(names(x), seq_len(n)) } out } # The output size varies each time we map f() x <- list(a = 1:4, b = letters[5:7], c = 8:9, d = letters[10]) x \%>\% lmap(maybe_rep) # We can apply f() on a selected subset of x x \%>\% lmap_at(c("a", "d"), maybe_rep) # Or only where a condition is satisfied x \%>\% lmap_if(is.character, maybe_rep) # A more realistic example would be a function that takes discrete # variables in a dataset and turns them into disjunctive tables, a # form that is amenable to fitting some types of models. # A disjunctive table contains only 0 and 1 but has as many columns # as unique values in the original variable. Ideally, we want to # combine the names of each level with the name of the discrete # variable in order to identify them. Given these requirements, it # makes sense to have a function that takes a data frame of size 1 # and returns a data frame of variable size. disjoin <- function(x, sep = "_") { name <- names(x) x <- as.factor(x[[1]]) out <- lapply(levels(x), function(level) { as.numeric(x == level) }) names(out) <- paste(name, levels(x), sep = sep) tibble::as_tibble(out) } # Now, we are ready to map disjoin() on each categorical variable of a # data frame: iris \%>\% lmap_if(is.factor, disjoin) mtcars \%>\% lmap_at(c("cyl", "vs", "am"), disjoin) } \seealso{ Other map variants: \code{\link{imap}}, \code{\link{invoke}}, \code{\link{map2}}, \code{\link{map}}, \code{\link{modify}} } purrr/man/rbernoulli.Rd0000644000176200001440000000070213124210513014632 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{rbernoulli} \alias{rbernoulli} \title{Generate random sample from a Bernoulli distribution} \usage{ rbernoulli(n, p = 0.5) } \arguments{ \item{n}{Number of samples} \item{p}{Probability of getting \code{TRUE}} } \value{ A logical vector } \description{ Generate random sample from a Bernoulli distribution } \examples{ rbernoulli(10) rbernoulli(100, 0.1) } purrr/man/every.Rd0000644000176200001440000000173513124210513013616 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/every-some.R \name{every} \alias{every} \alias{some} \title{Do every or some elements of a list satisfy a predicate?} \usage{ every(.x, .p, ...) some(.x, .p, ...) } \arguments{ \item{.x}{A list or atomic vector.} \item{.p}{A single predicate function, a formula describing such a predicate function, or a logical vector of the same length as \code{.x}. Alternatively, if the elements of \code{.x} are themselves lists of objects, a string indicating the name of a logical element in the inner lists. Only those elements where \code{.p} evaluates to \code{TRUE} will be modified.} \item{...}{Additional arguments passed on to \code{.f}.} } \value{ A logical vector of length 1. } \description{ Do every or some elements of a list satisfy a predicate? } \examples{ x <- list(0, 1, TRUE) x \%>\% every(identity) x \%>\% some(identity) y <- list(0:10, 5.5) y \%>\% every(is.numeric) y \%>\% every(is.integer) } purrr/man/head_while.Rd0000644000176200001440000000176113124210513014554 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/head-tail.R \name{head_while} \alias{head_while} \alias{tail_while} \title{Find head/tail that all satisfies a predicate.} \usage{ head_while(.x, .p, ...) tail_while(.x, .p, ...) } \arguments{ \item{.x}{A list or atomic vector.} \item{.p}{A single predicate function, a formula describing such a predicate function, or a logical vector of the same length as \code{.x}. Alternatively, if the elements of \code{.x} are themselves lists of objects, a string indicating the name of a logical element in the inner lists. Only those elements where \code{.p} evaluates to \code{TRUE} will be modified.} \item{...}{Additional arguments passed on to \code{.f}.} } \value{ A vector the same type as \code{.x}. } \description{ Find head/tail that all satisfies a predicate. } \examples{ pos <- function(x) x >= 0 head_while(5:-5, pos) tail_while(5:-5, negate(pos)) big <- function(x) x > 100 head_while(0:10, big) tail_while(0:10, big) } purrr/man/is_numeric.Rd0000644000176200001440000000126613123217317014630 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/predicates.R \name{is_numeric} \alias{is_numeric} \alias{is_scalar_numeric} \title{Test is an object is integer or double} \usage{ is_numeric(x) is_scalar_numeric(x) } \description{ Numeric is used in three different ways in base R: \itemize{ \item as an alias for double (as in \code{\link[=as.numeric]{as.numeric()}}) \item to mean either integer or double (as in \code{\link[=mode]{mode()}}) \item for something representable as numeric (as in \code{\link[=as.numeric]{as.numeric()}}) This function tests for the second, which is often not what you want so these functions are deprecated. } } \keyword{internal} purrr/man/when.Rd0000644000176200001440000000307413124211161013423 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/when.R \name{when} \alias{when} \title{Match/validate a set of conditions for an object and continue with the action associated with the first valid match.} \usage{ when(., ...) } \arguments{ \item{.}{the value to match against} \item{...}{formulas; each containing a condition as LHS and an action as RHS. named arguments will define additional values.} } \value{ The value resulting from the action of the first valid match/condition is returned. If no matches are found, and no default is given, NULL will be returned. Validity of the conditions are tested with \code{isTRUE}, or equivalently with \code{identical(condition, TRUE)}. In other words conditions resulting in more than one logical will never be valid. Note that the input value is always treated as a single object, as opposed to the \code{ifelse} function. } \description{ \code{when} is a flavour of pattern matching (or an if-else abstraction) in which a value is matched against a sequence of condition-action sets. When a valid match/condition is found the action is executed and the result of the action is returned. } \examples{ 1:10 \%>\% when( sum(.) <= 50 ~ sum(.), sum(.) <= 100 ~ sum(.)/2, ~ 0 ) 1:10 \%>\% when( sum(.) <= x ~ sum(.), sum(.) <= 2*x ~ sum(.)/2, ~ 0, x = 60 ) iris \%>\% subset(Sepal.Length > 10) \%>\% when( nrow(.) > 0 ~ ., ~ iris \%>\% head(10) ) iris \%>\% head \%>\% when(nrow(.) < 10 ~ ., ~ stop("Expected fewer than 10 rows.")) } \keyword{internal} purrr/man/as_mapper.Rd0000644000176200001440000000437313171451356014452 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/as_mapper.R \name{as_mapper} \alias{as_mapper} \alias{as_function} \alias{as_mapper.character} \alias{as_mapper.numeric} \alias{as_mapper.list} \title{Convert an object into a mapper function} \usage{ as_mapper(.f, ...) \method{as_mapper}{character}(.f, ..., .null, .default = NULL) \method{as_mapper}{numeric}(.f, ..., .null, .default = NULL) \method{as_mapper}{list}(.f, ..., .null, .default = NULL) } \arguments{ \item{.f}{A function, formula, or atomic vector. If a \strong{function}, it is used as is. If a \strong{formula}, e.g. \code{~ .x + 2}, it is converted to a function. There are three ways to refer to the arguments: \itemize{ \item For a single argument function, use \code{.} \item For a two argument function, use \code{.x} and \code{.y} \item For more arguments, use \code{..1}, \code{..2}, \code{..3} etc } This syntax allows you to create very compact anonymous functions. If \strong{character vector}, \strong{numeric vector}, or \strong{list}, it is converted to an extractor function. Character vectors index by name and numeric vectors index by position; use a list to index by position and name at different levels. Within a list, wrap strings in \code{\link[=get-attr]{get-attr()}} to extract named attributes. If a component is not present, the value of \code{.default} will be returned.} \item{...}{Additional arguments passed on to methods.} \item{.default, .null}{Optional additional argument for extractor functions (i.e. when \code{.f} is character, integer, or list). Returned when value is absent (does not exist) or empty (has length 0). \code{.null} is deprecated; please use \code{.default} instead.} } \description{ \code{as_mapper} is the powerhouse behind the varied function specifications that most purrr functions allow. It is an S3 generic. The default method forwards its arguments to \code{\link[rlang:as_function]{rlang::as_function()}}. } \examples{ as_mapper(~ . + 1) as_mapper(1) as_mapper(c("a", "b", "c")) # Equivalent to function(x) x[["a"]][["b"]][["c"]] as_mapper(list(1, "a", 2)) # Equivalent to function(x) x[[1]][["a"]][[2]] as_mapper(list(1, attr_getter("a"))) # Equivalent to function(x) attr(x[[1]], "a") as_mapper(c("a", "b", "c"), .null = NA) } purrr/man/map2.Rd0000644000176200001440000000777613171451356013354 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/map2-pmap.R \name{map2} \alias{map2} \alias{map2_lgl} \alias{map2_int} \alias{map2_dbl} \alias{map2_chr} \alias{map2_dfr} \alias{map2_dfc} \alias{map2_df} \alias{walk2} \alias{pmap} \alias{pmap_lgl} \alias{pmap_int} \alias{pmap_dbl} \alias{pmap_chr} \alias{pmap_dfr} \alias{pmap_dfc} \alias{pmap_df} \alias{pwalk} \title{Map over multiple inputs simultaneously.} \usage{ map2(.x, .y, .f, ...) map2_lgl(.x, .y, .f, ...) map2_int(.x, .y, .f, ...) map2_dbl(.x, .y, .f, ...) map2_chr(.x, .y, .f, ...) map2_dfr(.x, .y, .f, ..., .id = NULL) map2_dfc(.x, .y, .f, ...) walk2(.x, .y, .f, ...) pmap(.l, .f, ...) pmap_lgl(.l, .f, ...) pmap_int(.l, .f, ...) pmap_dbl(.l, .f, ...) pmap_chr(.l, .f, ...) pmap_dfr(.l, .f, ..., .id = NULL) pmap_dfc(.l, .f, ...) pwalk(.l, .f, ...) } \arguments{ \item{.x, .y}{Vectors of the same length. A vector of length 1 will be recycled.} \item{.f}{A function, formula, or atomic vector. If a \strong{function}, it is used as is. If a \strong{formula}, e.g. \code{~ .x + 2}, it is converted to a function. There are three ways to refer to the arguments: \itemize{ \item For a single argument function, use \code{.} \item For a two argument function, use \code{.x} and \code{.y} \item For more arguments, use \code{..1}, \code{..2}, \code{..3} etc } This syntax allows you to create very compact anonymous functions. If \strong{character vector}, \strong{numeric vector}, or \strong{list}, it is converted to an extractor function. Character vectors index by name and numeric vectors index by position; use a list to index by position and name at different levels. Within a list, wrap strings in \code{\link[=get-attr]{get-attr()}} to extract named attributes. If a component is not present, the value of \code{.default} will be returned.} \item{...}{Additional arguments passed on to \code{.f}.} \item{.id}{If not \code{NULL} a variable with this name will be created giving either the name or the index of the data frame.} \item{.l}{A list of lists. The length of \code{.l} determines the number of arguments that \code{.f} will be called with. List names will be used if present.} } \value{ An atomic vector, list, or data frame, depending on the suffix. Atomic vectors and lists will be named if \code{.x} or the first element of \code{.l} is named. If all input is length 0, the output will be length 0. If any input is length 1, it will be recycled to the length of the longest. } \description{ These functions are variants of \code{map()} iterate over multiple arguments in parallel. \code{map2()} and \code{walk2()} are specialised for the two argument case; \code{pmap()} and \code{pwalk()} allow you to provide any number of arguments in a list. } \details{ Note that arguments to be vectorised over come before the \code{.f}, and arguments that are supplied to every call come after \code{.f}. } \examples{ x <- list(1, 10, 100) y <- list(1, 2, 3) z <- list(5, 50, 500) map2(x, y, ~ .x + .y) # Or just map2(x, y, `+`) # Split into pieces, fit model to each piece, then predict by_cyl <- mtcars \%>\% split(.$cyl) mods <- by_cyl \%>\% map(~ lm(mpg ~ wt, data = .)) map2(mods, by_cyl, predict) pmap(list(x, y, z), sum) # Matching arguments by position pmap(list(x, y, z), function(a, b ,c) a / (b + c)) # Matching arguments by name l <- list(a = x, b = y, c = z) pmap(l, function(c, b, a) a / (b + c)) # Vectorizing a function over multiple arguments df <- data.frame( x = c("apple", "banana", "cherry"), pattern = c("p", "n", "h"), replacement = c("x", "f", "q"), stringsAsFactors = FALSE ) pmap(df, gsub) pmap_chr(df, gsub) ## Use `...` to absorb unused components of input list .l df <- data.frame( x = 1:3 + 0.1, y = 3:1 - 0.1, z = letters[1:3] ) plus <- function(x, y) x + y \dontrun{ ## this won't work pmap(df, plus) } ## but this will plus2 <- function(x, y, ...) x + y pmap_dbl(df, plus2) } \seealso{ Other map variants: \code{\link{imap}}, \code{\link{invoke}}, \code{\link{lmap}}, \code{\link{map}}, \code{\link{modify}} } purrr/man/accumulate.Rd0000644000176200001440000000376513147024610014622 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/reduce.R \name{accumulate} \alias{accumulate} \alias{accumulate_right} \title{Accumulate recursive folds across a list} \usage{ accumulate(.x, .f, ..., .init) accumulate_right(.x, .f, ..., .init) } \arguments{ \item{.x}{A list or atomic vector.} \item{.f}{For \code{reduce()}, a 2-argument function. The function will be passed the accumulated value as the first argument and the "next" value as the second argument. For \code{reduce2()}, a 3-argument function. The function will be passed the accumulated value as the first argument, the next value of \code{.x} as the second argument, and the next value of \code{.y} as the third argument.} \item{...}{Additional arguments passed on to \code{.f}.} \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.} } \description{ \code{accumulate} applies a function recursively over a list from the left, while \code{accumulate_right} applies the function from the right. Unlike \code{reduce} both functions keep the intermediate results. } \examples{ 1:3 \%>\% accumulate(`+`) 1:10 \%>\% accumulate_right(`*`) # From Haskell's scanl documentation 1:10 \%>\% accumulate(max, .init = 5) # Understanding the arguments .x and .y when .f # is a lambda function # .x is the accumulating value 1:10 \%>\% accumulate(~ .x) # .y is element in the list 1:10 \%>\% accumulate(~ .y) # Simulating stochastic processes with drift \dontrun{ library(dplyr) library(ggplot2) rerun(5, rnorm(100)) \%>\% set_names(paste0("sim", 1:5)) \%>\% map(~ accumulate(., ~ .05 + .x + .y)) \%>\% map_dfr(~ data_frame(value = .x, step = 1:100), .id = "simulation") \%>\% ggplot(aes(x = step, y = value)) + geom_line(aes(color = simulation)) + ggtitle("Simulations of a random walk with drift") } } purrr/man/imap.Rd0000644000176200001440000000443313171451356013426 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/imap.R \name{imap} \alias{imap} \alias{imap_lgl} \alias{imap_chr} \alias{imap_int} \alias{imap_dbl} \alias{imap_dfr} \alias{imap_dfc} \alias{iwalk} \title{Apply a function to each element of a vector, and its index} \usage{ imap(.x, .f, ...) imap_lgl(.x, .f, ...) imap_chr(.x, .f, ...) imap_int(.x, .f, ...) imap_dbl(.x, .f, ...) imap_dfr(.x, .f, ..., .id = NULL) imap_dfc(.x, .f, ..., .id = NULL) iwalk(.x, .f, ...) } \arguments{ \item{.x}{A list or atomic vector.} \item{.f}{A function, formula, or atomic vector. If a \strong{function}, it is used as is. If a \strong{formula}, e.g. \code{~ .x + 2}, it is converted to a function. There are three ways to refer to the arguments: \itemize{ \item For a single argument function, use \code{.} \item For a two argument function, use \code{.x} and \code{.y} \item For more arguments, use \code{..1}, \code{..2}, \code{..3} etc } This syntax allows you to create very compact anonymous functions. If \strong{character vector}, \strong{numeric vector}, or \strong{list}, it is converted to an extractor function. Character vectors index by name and numeric vectors index by position; use a list to index by position and name at different levels. Within a list, wrap strings in \code{\link[=get-attr]{get-attr()}} to extract named attributes. If a component is not present, the value of \code{.default} will be returned.} \item{...}{Additional arguments passed on to \code{.f}.} \item{.id}{If not \code{NULL} a variable with this name will be created giving either the name or the index of the data frame.} } \value{ A vector the same length as \code{.x}. } \description{ \code{imap_xxx(x, ...)}, an indexed map, is short hand for \code{map2(x, names(x), ...)} if \code{x} has names, or \code{map2(x, seq_along(x), ...)} if it does not. This is useful if you need to compute on both the value and the position of an element. } \examples{ # Note that when using the formula shortcut, the first argument # is the value, and the second is the position imap_chr(sample(10), ~ paste0(.y, ": ", .x)) iwalk(mtcars, ~ cat(.y, ": ", median(.x), "\\n", sep = "")) } \seealso{ Other map variants: \code{\link{invoke}}, \code{\link{lmap}}, \code{\link{map2}}, \code{\link{map}}, \code{\link{modify}} } purrr/man/lift.Rd0000644000176200001440000001362213124210513013420 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/composition.R \name{lift} \alias{lift} \alias{lift} \alias{lift_dl} \alias{lift_dv} \alias{lift_vl} \alias{lift_vd} \alias{lift_ld} \alias{lift_lv} \title{Lift the domain of a function} \usage{ 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, ...) } \arguments{ \item{..f}{A function to lift.} \item{...}{Default arguments for \code{..f}. These will be evaluated only once, when the lifting factory is called.} \item{.unnamed}{If \code{TRUE}, \code{ld} or \code{lv} will not name the parameters in the lifted function signature. This prevents matching of arguments by name and match by position instead.} \item{.type}{A vector mold or a string describing the type of the input vectors. The latter can be any of the types returned by \code{\link[=typeof]{typeof()}}, or "numeric" as a shorthand for either "double" or "integer".} } \value{ A function. } \description{ \code{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, \code{lift_ld(fun)} transforms a function taking a list to a function taking dots. } \details{ The most important of those helpers is probably \code{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, \code{lift()} is provided as an alias for that operation. } \section{from ... to \code{list(...)} or \code{c(...)}}{ Here dots should be taken here in a figurative way. The lifted functions does not need to take dots per se. The function is simply wrapped a function in \code{\link[=do.call]{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. } \section{from \code{c(...)} to \code{list(...)} or \code{...}}{ These factories allow a function taking a vector to take a list or dots instead. The lifted function internally transforms its inputs back to an atomic vector. purrr does not obey the usual R casting rules (e.g., \code{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 \code{.type}. } \section{from list(...) to c(...) or ...}{ \code{lift_ld()} turns a function that takes a list into a function that takes dots. \code{lift_vd()} does the same with a function that takes an atomic vector. These factory functions are the inverse operations of \code{lift_dl()} and \code{lift_dv()}. \code{lift_vd()} internally coerces the inputs of \code{..f} to an atomic vector. The details of this coercion can be controlled with \code{.type}. } \examples{ ### 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 ... # 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() 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_dots <- lift_ld(cross) out1 <- cross(list(a = 1:2, b = c("a", "b", "c"))) out2 <- cross_dots(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, .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, .) } \dontrun{ letters \%>\% glue() # fails because glue() expects a list} letters \%>\% lift_lv(glue)() # succeeds } \seealso{ \code{\link[=invoke]{invoke()}} } purrr/man/array-coercion.Rd0000644000176200001440000000372713075737303015424 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/arrays.R \name{array-coercion} \alias{array-coercion} \alias{array_branch} \alias{array_tree} \title{Coerce array to list} \usage{ array_branch(array, margin = NULL) array_tree(array, margin = NULL) } \arguments{ \item{array}{An array to coerce into a list.} \item{margin}{A numeric vector indicating the positions of the indices to be to be enlisted. If \code{NULL}, a full margin is used. If \code{numeric(0)}, the array as a whole is wrapped in a list.} } \description{ \code{array_branch()} and \code{array_tree()} enable arrays to be used with purrr's functionals by turning them into lists. The details of the coercion are controlled by the \code{margin} argument. \code{array_tree()} creates an hierarchical list (a tree) that has as many levels as dimensions specified in \code{margin}, while \code{array_branch()} creates a flat list (by analogy, a branch) along all mentioned dimensions. } \details{ When no margin is specified, all dimensions are used by default. When \code{margin} is a numeric vector of length zero, the whole array is wrapped in a list. } \examples{ # We create an array with 3 dimensions x <- array(1:12, c(2, 2, 3)) # A full margin for such an array would be the vector 1:3. This is # the default if you don't specify a margin # Creating a branch along the full margin is equivalent to # as.list(array) and produces a list of size length(x): array_branch(x) \%>\% str() # A branch along the first dimension yields a list of length 2 # with each element containing a 2x3 array: array_branch(x, 1) \%>\% str() # A branch along the first and third dimensions yields a list of # length 2x3 whose elements contain a vector of length 2: array_branch(x, c(1, 3)) \%>\% str() # Creating a tree from the full margin creates a list of lists of # lists: array_tree(x) \%>\% str() # The ordering and the depth of the tree are controlled by the # margin argument: array_tree(x, c(3, 1)) \%>\% str() } purrr/man/reexports.Rd0000644000176200001440000000437313147024405014530 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/predicates.R \docType{import} \name{reexports} \alias{reexports} \alias{is_bare_list} \alias{reexports} \alias{is_bare_atomic} \alias{reexports} \alias{is_bare_vector} \alias{reexports} \alias{is_bare_double} \alias{reexports} \alias{is_bare_integer} \alias{reexports} \alias{is_bare_numeric} \alias{reexports} \alias{is_bare_character} \alias{reexports} \alias{is_bare_logical} \alias{reexports} \alias{is_list} \alias{reexports} \alias{is_atomic} \alias{reexports} \alias{is_vector} \alias{reexports} \alias{is_integer} \alias{reexports} \alias{is_double} \alias{reexports} \alias{is_character} \alias{reexports} \alias{is_logical} \alias{reexports} \alias{is_null} \alias{reexports} \alias{is_function} \alias{reexports} \alias{is_scalar_list} \alias{reexports} \alias{is_scalar_atomic} \alias{reexports} \alias{is_scalar_vector} \alias{reexports} \alias{is_scalar_double} \alias{reexports} \alias{is_scalar_character} \alias{reexports} \alias{is_scalar_logical} \alias{reexports} \alias{is_scalar_integer} \alias{reexports} \alias{is_empty} \alias{reexports} \alias{is_formula} \title{Objects exported from other packages} \keyword{internal} \description{ These objects are imported from other packages. Follow the links below to see their documentation. \describe{ \item{rlang}{\code{\link[rlang]{is_bare_list}}, \code{\link[rlang]{is_bare_atomic}}, \code{\link[rlang]{is_bare_vector}}, \code{\link[rlang]{is_bare_double}}, \code{\link[rlang]{is_bare_integer}}, \code{\link[rlang]{is_bare_numeric}}, \code{\link[rlang]{is_bare_character}}, \code{\link[rlang]{is_bare_logical}}, \code{\link[rlang]{is_list}}, \code{\link[rlang]{is_atomic}}, \code{\link[rlang]{is_vector}}, \code{\link[rlang]{is_integer}}, \code{\link[rlang]{is_double}}, \code{\link[rlang]{is_character}}, \code{\link[rlang]{is_logical}}, \code{\link[rlang]{is_null}}, \code{\link[rlang]{is_function}}, \code{\link[rlang]{is_scalar_list}}, \code{\link[rlang]{is_scalar_atomic}}, \code{\link[rlang]{is_scalar_vector}}, \code{\link[rlang]{is_scalar_double}}, \code{\link[rlang]{is_scalar_character}}, \code{\link[rlang]{is_scalar_logical}}, \code{\link[rlang]{is_scalar_integer}}, \code{\link[rlang]{is_empty}}, \code{\link[rlang]{is_formula}}} }} purrr/man/purrr-package.Rd0000644000176200001440000000132513147024610015230 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/purrr.R \docType{package} \name{purrr-package} \alias{purrr} \alias{purrr-package} \title{purrr: Functional Programming Tools} \description{ A complete and consistent functional programming toolkit for R. } \seealso{ Useful links: \itemize{ \item \url{http://purrr.tidyverse.org} \item \url{https://github.com/tidyverse/purrr} \item Report bugs at \url{https://github.com/tidyverse/purrr/issues} } } \author{ \strong{Maintainer}: Lionel Henry \email{lionel@rstudio.com} Authors: \itemize{ \item Hadley Wickham \email{hadley@rstudio.com} } Other contributors: \itemize{ \item RStudio [copyright holder, funder] } } \keyword{internal} purrr/man/as_vector.Rd0000644000176200001440000000322213124210513014442 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/coercion.R \name{as_vector} \alias{as_vector} \alias{simplify} \alias{simplify_all} \title{Coerce a list to a vector} \usage{ as_vector(.x, .type = NULL) simplify(.x, .type = NULL) simplify_all(.x, .type = NULL) } \arguments{ \item{.x}{A list of vectors} \item{.type}{A vector mold or a string describing the type of the input vectors. The latter can be any of the types returned by \code{\link[=typeof]{typeof()}}, or "numeric" as a shorthand for either "double" or "integer".} } \description{ \code{as_vector()} collapses a list of vectors into one vector. It checks that the type of each vector is consistent with \code{.type}. If the list can not be simplified, it throws an error. \code{simplify} will simplify a vector if possible; \code{simplify_all} will apply \code{simplify} to every element of a list. } \details{ \code{.type} can be a vector mold specifying both the type and the length of the vectors to be concatenated, such as \code{numeric(1)} or \code{integer(4)}. Alternatively, it can be a string describing the type, one of: "logical", "integer", "double", "complex", "character" or "raw". } \examples{ # Supply the type either with a string: as.list(letters) \%>\% as_vector("character") # Or with a vector mold: as.list(letters) \%>\% as_vector(character(1)) # Vector molds are more flexible because they also specify the # length of the concatenated vectors: list(1:2, 3:4, 5:6) \%>\% as_vector(integer(2)) # Note that unlike vapply(), as_vector() never adds dimension # attributes. So when you specify a vector mold of size > 1, you # always get a vector and not a matrix } purrr/man/reduce.Rd0000644000176200001440000000377513124210513013741 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/reduce.R \name{reduce} \alias{reduce} \alias{reduce_right} \alias{reduce2} \alias{reduce2_right} \title{Reduce a list to a single value by iteratively applying a binary function.} \usage{ reduce(.x, .f, ..., .init) reduce_right(.x, .f, ..., .init) reduce2(.x, .y, .f, ..., .init) reduce2_right(.x, .y, .f, ..., .init) } \arguments{ \item{.x}{A list or atomic vector.} \item{.f}{For \code{reduce()}, a 2-argument function. The function will be passed the accumulated value as the first argument and the "next" value as the second argument. For \code{reduce2()}, a 3-argument function. The function will be passed the accumulated value as the first argument, the next value of \code{.x} as the second argument, and the next value of \code{.y} as the third argument.} \item{...}{Additional arguments passed on to \code{.f}.} \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{.y}{For \code{reduce2()}, an additional argument that is passed to \code{.f}. If \code{init} is not set, \code{.y} should be 1 element shorter than \code{.x}.} } \description{ \code{reduce()} combines from the left, \code{reduce_right()} combines from the right. \code{reduce(list(x1, x2, x3), f)} is equivalent to \code{f(f(x1, x2), x3)}; \code{reduce_right(list(x1, x2, x3), f)} is equivalent to \code{f(f(x3, x2), x1)}. } \examples{ 1:3 \%>\% reduce(`+`) 1:10 \%>\% reduce(`*`) paste2 <- function(x, y, sep = ".") paste(x, y, sep = sep) letters[1:4] \%>\% reduce(paste2) letters[1:4] \%>\% reduce2(c("-", ".", "-"), paste2) samples <- rerun(2, sample(10, 5)) samples reduce(samples, union) reduce(samples, intersect) x <- list(c(0, 1), c(2, 3), c(4, 5)) x \%>\% reduce(c) x \%>\% reduce_right(c) # Equivalent to: x \%>\% rev() \%>\% reduce(c) } purrr/man/vec_depth.Rd0000644000176200001440000000067613137651733014451 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/depth.R \name{vec_depth} \alias{vec_depth} \title{Compute the depth of a vector} \usage{ vec_depth(x) } \arguments{ \item{x}{A vector} } \value{ An integer. } \description{ The depth of a vector is basically how many levels that you can index into it. } \examples{ x <- list( list(), list(list()), list(list(list(1))) ) vec_depth(x) x \%>\% map_int(vec_depth) } purrr/man/get-attr.Rd0000644000176200001440000000053713075737303014232 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{get-attr} \alias{get-attr} \alias{\%@\%} \title{Infix attribute accessor} \usage{ x \%@\% name } \arguments{ \item{x}{Object} \item{name}{Attribute name} } \description{ Infix attribute accessor } \examples{ factor(1:3) \%@\% "levels" mtcars \%@\% "class" } purrr/man/pluck.Rd0000644000176200001440000000606013147024610013604 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/as_mapper.R \name{pluck} \alias{pluck} \alias{attr_getter} \title{Pluck out a single an element from a vector or environment} \usage{ pluck(.x, ..., .default = NULL) attr_getter(attr) } \arguments{ \item{.x}{A vector or environment} \item{...}{A list of accessors for indexing into the object. Can be an integer position, a string name, or an accessor function. If the object being indexed is an S4 object, accessing it by name will return the corresponding slot. These dots \link[rlang:dots_splice]{splice lists automatically}. This means you can supply arguments and lists of arguments indistinctly.} \item{.default}{Value to use if target is empty or absent.} \item{attr}{An attribute name as string.} } \description{ This is a generalised form of \code{[[} which allows you to index deeply and flexibly into data structures. It supports R standard accessors like integer positions and string names, and also accepts arbitrary accessor functions, i.e. functions that take an object and return some internal piece. \code{pluck()} is often more readable than a mix of operators and accessors because it reads linearly and is free of syntactic cruft. Compare: \code{accessor(x[[1]])$foo} to \code{pluck(x, 1, accessor, "foo")}. Furthermore, \code{pluck()} never partial-matches unlike \code{$} which will select the \code{disp} object if you write \code{mtcars$di}. } \details{ Since it handles arbitrary accessor functions, \code{pluck()} is a type of composition operator. However, it is indexing-oriented thanks to its handling of strings and integers. By the same token is also explicit regarding the intent of the composition (e.g. extraction). } \examples{ # pluck() supports integer positions, string names, and functions. # Using functions, you can easily extend pluck(). Let's create a # list of data structures: obj1 <- list("a", list(1, elt = "foobar")) obj2 <- list("b", list(2, elt = "foobaz")) x <- list(obj1, obj2) # And now an accessor for these complex data structures: my_element <- function(x) x[[2]]$elt # The accessor can then be passed to pluck: pluck(x, 1, my_element) pluck(x, 2, my_element) # Even for this simple data structure, this is more readable than # the alternative form because it requires you to read both from # right-to-left and from left-to-right in different parts of the # expression: my_element(x[[1]]) # This technique is used for plucking into attributes with # attr_getter(). It takes an attribute name and returns a function # to access the attribute: obj1 <- structure("obj", obj_attr = "foo") obj2 <- structure("obj", obj_attr = "bar") x <- list(obj1, obj2) # pluck() is handy for extracting deeply into a data structure. # Here we'll first extract by position, then by attribute: pluck(x, 1, attr_getter("obj_attr")) # From first object pluck(x, 2, attr_getter("obj_attr")) # From second object # pluck() splices lists of arguments automatically. The following # pluck is equivalent to the one above: idx <- list(1, attr_getter("obj_attr")) pluck(x, idx) } \keyword{internal} purrr/LICENSE0000644000176200001440000010451312601275136012440 0ustar liggesusers GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read .