diff --git a/DESCRIPTION b/DESCRIPTION index f48e7b5b..838a54e9 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -26,3 +26,4 @@ VignetteBuilder: knitr LazyData: true Roxygen: list(markdown = TRUE) RoxygenNote: 6.0.1 +Encoding: UTF-8 diff --git a/NAMESPACE b/NAMESPACE index 2671c754..48e9212f 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -54,6 +54,7 @@ export(flatten_dfc) export(flatten_dfr) export(flatten_int) export(flatten_lgl) +export(flatten_raw) export(has_element) export(head_while) export(imap) @@ -63,6 +64,7 @@ export(imap_dfc) export(imap_dfr) export(imap_int) export(imap_lgl) +export(imap_raw) export(invoke) export(invoke_map) export(invoke_map_chr) @@ -72,6 +74,7 @@ export(invoke_map_dfc) export(invoke_map_dfr) export(invoke_map_int) export(invoke_map_lgl) +export(invoke_map_raw) export(is_atomic) export(is_bare_atomic) export(is_bare_character) @@ -124,6 +127,7 @@ export(map2_dfc) export(map2_dfr) export(map2_int) export(map2_lgl) +export(map2_raw) export(map_at) export(map_call) export(map_chr) @@ -134,6 +138,7 @@ export(map_dfr) export(map_if) export(map_int) export(map_lgl) +export(map_raw) export(modify) export(modify_at) export(modify_depth) @@ -149,6 +154,7 @@ export(pmap_dfc) export(pmap_dfr) export(pmap_int) export(pmap_lgl) +export(pmap_raw) export(possibly) export(prepend) export(pwalk) diff --git a/NEWS.md b/NEWS.md index 073082d5..d601fdbf 100644 --- a/NEWS.md +++ b/NEWS.md @@ -22,6 +22,9 @@ * `list_modify()`, `update_list()` and `list_merge()` now handle duplicate duplicate argument names correctly (#441, @mgirlich). + +* `map_raw`, `imap_raw`, `flatten_raw`, `invoke_map_raw`, `map2_raw` and `pmap_raw` + added to support raw vectors. (#455, @romainfrancois) # purrr 0.2.4 diff --git a/R/coerce.R b/R/coerce.R index 613631d8..3c01935b 100644 --- a/R/coerce.R +++ b/R/coerce.R @@ -8,3 +8,4 @@ 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") +coerce_raw <- function(x) coerce(x, "raw") diff --git a/R/flatten.R b/R/flatten.R index 9ff5a31b..c43cb10f 100644 --- a/R/flatten.R +++ b/R/flatten.R @@ -54,6 +54,12 @@ flatten_chr <- function(.x) { .Call(vflatten_impl, .x, "character") } +#' @export +#' @rdname flatten +flatten_raw <- function(.x) { + .Call(vflatten_impl, .x, "raw") +} + #' @export #' @rdname flatten flatten_dfr <- function(.x, .id = NULL) { diff --git a/R/imap.R b/R/imap.R index 6b883c91..97684f29 100644 --- a/R/imap.R +++ b/R/imap.R @@ -47,6 +47,13 @@ imap_dbl <- function(.x, .f, ...) { map2_dbl(.x, vec_index(.x), .f, ...) } +#' @rdname imap +#' @export +imap_raw <- function(.x, .f, ...) { + .f <- as_mapper(.f, ...) + map2_raw(.x, vec_index(.x), .f, ...) +} + #' @rdname imap #' @export imap_dfr <- function(.x, .f, ..., .id = NULL) { diff --git a/R/invoke.R b/R/invoke.R index 9873233c..98414fca 100644 --- a/R/invoke.R +++ b/R/invoke.R @@ -113,6 +113,13 @@ invoke_map_chr <- function(.f, .x = list(NULL), ..., .env = NULL) { .f <- as_invoke_function(.f) map2_chr(.f, .x, invoke, ..., .env = .env) } +#' @rdname invoke +#' @export +invoke_map_raw <- function(.f, .x = list(NULL), ..., .env = NULL) { + .env <- .env %||% parent.frame() + .f <- as_invoke_function(.f) + map2_raw(.f, .x, invoke, ..., .env = .env) +} #' @rdname invoke #' @export diff --git a/R/map.R b/R/map.R index 2619f1fe..9f7a85ad 100644 --- a/R/map.R +++ b/R/map.R @@ -155,6 +155,13 @@ map_dbl <- function(.x, .f, ...) { .Call(map_impl, environment(), ".x", ".f", "double") } +#' @rdname map +#' @export +map_raw <- function(.x, .f, ...) { + .f <- as_mapper(.f, ...) + .Call(map_impl, environment(), ".x", ".f", "raw") +} + #' @rdname map #' @param .id Either a string or `NULL`. If a string, the output will contain #' a variable with that name, storing either the name (if `.x` is named) or diff --git a/R/map2-pmap.R b/R/map2-pmap.R index f565760b..a379fde0 100644 --- a/R/map2-pmap.R +++ b/R/map2-pmap.R @@ -99,6 +99,12 @@ map2_chr <- function(.x, .y, .f, ...) { .f <- as_mapper(.f, ...) .Call(map2_impl, environment(), ".x", ".y", ".f", "character") } +#' @export +#' @rdname map2 +map2_raw <- function(.x, .y, .f, ...) { + .f <- as_mapper(.f, ...) + .Call(map2_impl, environment(), ".x", ".y", ".f", "raw") +} #' @rdname map2 #' @export map2_dfr <- function(.x, .y, .f, ..., .id = NULL) { @@ -183,6 +189,16 @@ pmap_chr <- function(.l, .f, ...) { .Call(pmap_impl, environment(), ".l", ".f", "character") } +#' @export +#' @rdname map2 +pmap_raw <- function(.l, .f, ...) { + .f <- as_mapper(.f, ...) + if (is.data.frame(.l)) { + .l <- as.list(.l) + } + + .Call(pmap_impl, environment(), ".l", ".f", "raw") +} #' @rdname map2 #' @export diff --git a/man/accumulate.Rd b/man/accumulate.Rd index 24a5aacb..20152dae 100644 --- a/man/accumulate.Rd +++ b/man/accumulate.Rd @@ -27,10 +27,13 @@ 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.} } +\value{ +A vector the same length of \code{.x} with the same names as \code{.x} +} \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 and the names of \code{.x}. +both functions keep the intermediate results. } \examples{ 1:3 \%>\% accumulate(`+`) diff --git a/man/flatten.Rd b/man/flatten.Rd index 61c64f66..b6765d73 100644 --- a/man/flatten.Rd +++ b/man/flatten.Rd @@ -6,6 +6,7 @@ \alias{flatten_int} \alias{flatten_dbl} \alias{flatten_chr} +\alias{flatten_raw} \alias{flatten_dfr} \alias{flatten_dfc} \alias{flatten_df} @@ -21,6 +22,8 @@ flatten_dbl(.x) flatten_chr(.x) +flatten_raw(.x) + flatten_dfr(.x, .id = NULL) flatten_dfc(.x) diff --git a/man/imap.Rd b/man/imap.Rd index 31320660..ea051c24 100644 --- a/man/imap.Rd +++ b/man/imap.Rd @@ -6,6 +6,7 @@ \alias{imap_chr} \alias{imap_int} \alias{imap_dbl} +\alias{imap_raw} \alias{imap_dfr} \alias{imap_dfc} \alias{iwalk} @@ -21,6 +22,8 @@ imap_int(.x, .f, ...) imap_dbl(.x, .f, ...) +imap_raw(.x, .f, ...) + imap_dfr(.x, .f, ..., .id = NULL) imap_dfc(.x, .f, ...) diff --git a/man/invoke.Rd b/man/invoke.Rd index 676a9c56..9214662e 100644 --- a/man/invoke.Rd +++ b/man/invoke.Rd @@ -7,6 +7,7 @@ \alias{invoke_map_int} \alias{invoke_map_dbl} \alias{invoke_map_chr} +\alias{invoke_map_raw} \alias{invoke_map_dfr} \alias{invoke_map_dfc} \alias{invoke_map_df} @@ -25,6 +26,8 @@ invoke_map_dbl(.f, .x = list(NULL), ..., .env = NULL) invoke_map_chr(.f, .x = list(NULL), ..., .env = NULL) +invoke_map_raw(.f, .x = list(NULL), ..., .env = NULL) + invoke_map_dfr(.f, .x = list(NULL), ..., .env = NULL) invoke_map_dfc(.f, .x = list(NULL), ..., .env = NULL) diff --git a/man/map.Rd b/man/map.Rd index e36068e1..2d008de7 100644 --- a/man/map.Rd +++ b/man/map.Rd @@ -8,6 +8,7 @@ \alias{map_chr} \alias{map_int} \alias{map_dbl} +\alias{map_raw} \alias{map_dfr} \alias{map_df} \alias{map_dfc} @@ -28,6 +29,8 @@ map_int(.x, .f, ...) map_dbl(.x, .f, ...) +map_raw(.x, .f, ...) + map_dfr(.x, .f, ..., .id = NULL) map_dfc(.x, .f, ...) diff --git a/man/map2.Rd b/man/map2.Rd index 71997498..f93da082 100644 --- a/man/map2.Rd +++ b/man/map2.Rd @@ -6,6 +6,7 @@ \alias{map2_int} \alias{map2_dbl} \alias{map2_chr} +\alias{map2_raw} \alias{map2_dfr} \alias{map2_dfc} \alias{map2_df} @@ -15,6 +16,7 @@ \alias{pmap_int} \alias{pmap_dbl} \alias{pmap_chr} +\alias{pmap_raw} \alias{pmap_dfr} \alias{pmap_dfc} \alias{pmap_df} @@ -31,6 +33,8 @@ map2_dbl(.x, .y, .f, ...) map2_chr(.x, .y, .f, ...) +map2_raw(.x, .y, .f, ...) + map2_dfr(.x, .y, .f, ..., .id = NULL) map2_dfc(.x, .y, .f, ...) @@ -47,6 +51,8 @@ pmap_dbl(.l, .f, ...) pmap_chr(.l, .f, ...) +pmap_raw(.l, .f, ...) + pmap_dfr(.l, .f, ..., .id = NULL) pmap_dfc(.l, .f, ...) diff --git a/src/coerce.c b/src/coerce.c index b6831d10..86ea0055 100644 --- a/src/coerce.c +++ b/src/coerce.c @@ -3,6 +3,15 @@ #include #include +const char* sixteen = "0123456789abcdef" ; + +SEXP raw_to_char( Rbyte x){ + char buf[2] ; + buf[0] = sixteen[ x >> 4] ; + buf[1] = sixteen[ x & 0x0F ] ; + return Rf_mkCharLen( buf, 2) ; +} + double logical_to_real(int x) { return (x == NA_LOGICAL) ? NA_REAL : x; } @@ -58,6 +67,7 @@ void set_vector_value(SEXP to, int i, SEXP from, int j) { switch(TYPEOF(from)) { case LGLSXP: INTEGER(to)[i] = LOGICAL(from)[j]; break; case INTSXP: INTEGER(to)[i] = INTEGER(from)[j]; break; + case RAWSXP: INTEGER(to)[i] = RAW(from)[j]; break ; default: cant_coerce(from, to, i); } break; @@ -66,6 +76,7 @@ void set_vector_value(SEXP to, int i, SEXP from, int j) { 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; + case RAWSXP: REAL(to)[i] = RAW(from)[j]; break ; default: cant_coerce(from, to, i); } break; @@ -75,12 +86,19 @@ void set_vector_value(SEXP to, int i, SEXP from, int j) { 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; + case RAWSXP: SET_STRING_ELT(to, i, raw_to_char(RAW(from)[j])); break; default: cant_coerce(from, to, i); } break; case VECSXP: SET_VECTOR_ELT(to, i, from); break; + case RAWSXP: + switch(TYPEOF(from)) { + case RAWSXP: RAW(to)[i] = RAW(from)[j]; break; + default: cant_coerce(from, to, i); + } + break ; default: cant_coerce(from, to, i); } } diff --git a/src/extract.c b/src/extract.c index e0b898a1..1ed41c77 100644 --- a/src/extract.c +++ b/src/extract.c @@ -77,6 +77,7 @@ SEXP extract_vector(SEXP x, SEXP index_i, int i) { case REALSXP: return Rf_ScalarReal(REAL(x)[offset]); case STRSXP: return Rf_ScalarString(STRING_ELT(x, offset)); case VECSXP: return VECTOR_ELT(x, offset); + case RAWSXP: return Rf_ScalarRaw(RAW(x)[offset]) ; default: Rf_errorcall(R_NilValue, "Don't know how to index object of type %s at level %i", diff --git a/tests/testthat/test-as-mapper.R b/tests/testthat/test-as-mapper.R index 7cfcf1e3..ef3c1a26 100644 --- a/tests/testthat/test-as-mapper.R +++ b/tests/testthat/test-as-mapper.R @@ -71,7 +71,10 @@ test_that("lists are wrapped", { expect_identical(mapper_list, base_list) }) -test_that("raw and complex types aren't supported for indexing", { - expect_error(as_mapper(1)(raw(2))) +test_that("complex types aren't supported for indexing", { expect_error(as_mapper(1)(complex(2))) }) + +test_that("raw vectors are supported for indexing", { + expect_equal( as_mapper(1)(raw(2)), raw(1) ) +}) diff --git a/tests/testthat/test-coerce.R b/tests/testthat/test-coerce.R index 10e1f9ba..f67b3a27 100644 --- a/tests/testthat/test-coerce.R +++ b/tests/testthat/test-coerce.R @@ -16,15 +16,21 @@ test_that("can't coerce downwards", { 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_raw(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_raw("a"), "Can't coerce") expect_error(coerce_int(1), "Can't coerce") expect_error(coerce_lgl(1), "Can't coerce") + expect_error(coerce_raw(1), "Can't coerce") expect_error(coerce_lgl(1L), "Can't coerce") + expect_error(coerce_raw(1L), "Can't coerce") + + expect_error(coerce_raw(TRUE), "Can't coerce") }) test_that("coercing to same type returns input", { @@ -32,6 +38,7 @@ test_that("coercing to same type returns input", { 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")) + expect_equal(coerce_raw(as.raw(c(0,1))), as.raw(c(0,1))) }) test_that("types automatically coerced upwards", { diff --git a/tests/testthat/test-flatten.R b/tests/testthat/test-flatten.R index d807a1df..c0c55fc8 100644 --- a/tests/testthat/test-flatten.R +++ b/tests/testthat/test-flatten.R @@ -19,6 +19,7 @@ test_that("can flatten all atomic vectors", { expect_equal(flatten(list(1L)), list(1L)) expect_equal(flatten(list(1)), list(1)) expect_equal(flatten(list("a")), list("a")) + expect_equal(flatten_raw(list(as.raw(1))), as.raw(1)) }) test_that("NULLs are silently dropped", { diff --git a/tests/testthat/test-imap.R b/tests/testthat/test-imap.R index 05b18b9d..7574d168 100644 --- a/tests/testthat/test-imap.R +++ b/tests/testthat/test-imap.R @@ -15,6 +15,7 @@ test_that("atomic vector imap works", { 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) + expect_equal(imap_raw(as.raw(12), rawShift), rawShift(as.raw(12), 1) ) }) test_that("data frame imap works", { diff --git a/tests/testthat/test-invoke.R b/tests/testthat/test-invoke.R index 23787f0d..3df8be9e 100644 --- a/tests/testthat/test-invoke.R +++ b/tests/testthat/test-invoke.R @@ -33,6 +33,8 @@ test_that("invoke_map() works with bare function", { expect_identical(invoke_map_int(`+`, data), c(3L, 7L)) expect_identical(invoke_map_lgl(`&&`, data), c(TRUE, TRUE)) + expect_identical(invoke_map_raw(identity, as.raw(1:3)), as.raw(1:3)) + ops <- set_names(c(`+`, `-`), c("a", "b")) expect_identical(invoke_map_dfr(ops, data), invoke_map_dfc(ops, data)) }) diff --git a/tests/testthat/test-map.R b/tests/testthat/test-map.R index cb73208b..0f88a71b 100644 --- a/tests/testthat/test-map.R +++ b/tests/testthat/test-map.R @@ -29,13 +29,20 @@ test_that("map() always returns a list", { test_that("types automatically coerced upwards", { expect_identical(map_int(c(FALSE, TRUE), identity), c(0L, 1L)) + expect_identical(map_int(as.raw(0:1), identity), 0:1) 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_dbl(as.raw(0:1), identity), c(0, 1)) 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")) + expect_identical(map_chr(as.raw(0:255), identity), as.character(as.raw(0:255))) +}) + +test_that("map_raw",{ + expect_equal(map_raw("a", charToRaw), charToRaw("a")) }) test_that("logical and integer NA become correct double NA", { diff --git a/tests/testthat/test-map_n.R b/tests/testthat/test-map_n.R index a7fcb600..f0012656 100644 --- a/tests/testthat/test-map_n.R +++ b/tests/testthat/test-map_n.R @@ -46,6 +46,7 @@ test_that("outputs are suffixes have correct type", { 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_raw(list(x), as.raw), "raw") expect_is(pmap_dfr(list(x), as.data.frame), "data.frame") expect_is(pmap_dfc(list(x), as.data.frame), "data.frame") }) @@ -57,4 +58,5 @@ test_that("pmap on data frames performs rowwise operations", { 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") + expect_is(pmap_raw(mtcars2, function(mpg, cyl) as.raw(cyl)), "raw") }) diff --git a/tests/testthat/test-pluck.R b/tests/testthat/test-pluck.R index 135ad70f..71958a63 100644 --- a/tests/testthat/test-pluck.R +++ b/tests/testthat/test-pluck.R @@ -165,3 +165,7 @@ test_that("pluck returns missing with missing index", { test_that("plucks by name", { expect_equal(pluck(A, list("a")), 1) }) + +test_that("can't pluck from complex", { + expect_error( pluck( 1+2i, 1 ), "Don't know how to index object of type complex at level 1" ) +})