Skip to content

Commit 1aa0d0e

Browse files
committed
Implement list_pall() and list_pany()
1 parent a5c1fe3 commit 1aa0d0e

File tree

11 files changed

+1092
-0
lines changed

11 files changed

+1092
-0
lines changed

NAMESPACE

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,8 @@ export(list_check_all_vectors)
468468
export(list_combine)
469469
export(list_drop_empty)
470470
export(list_of)
471+
export(list_pall)
472+
export(list_pany)
471473
export(list_sizes)
472474
export(list_unchop)
473475
export(maybe_lossy_cast)

NEWS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# vctrs (development version)
22

3+
* New `list_pany()` and `list_pall()`, parallel variants of `any()` and `all()`
4+
(in the same way that `pmin()` and `pmax()` are parallel variants of `min()`
5+
and `max()`).
6+
37
* Experimental "partial" type support has been removed. This idea never panned out and was not widely used. The following functions have been removed (#2101):
48

59
* `is_partial()`

R/parallel.R

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#' Parallel `any()` and `all()`
2+
#'
3+
#' @description
4+
#' These functions are variants of [any()] and [all()] that work in parallel on
5+
#' multiple inputs at once. They work similarly to how [pmin()] and [pmax()] are
6+
#' parallel variants of [min()] and [max()].
7+
#'
8+
#' @inheritParams rlang::args_dots_empty
9+
#' @inheritParams rlang::args_error_context
10+
#'
11+
#' @param x A list of logical vectors of equal size.
12+
#'
13+
#' @param missing Handling of missing values. One of:
14+
#'
15+
#' - `NULL`, no special behavior is applied. Missings are treated the same way
16+
#' as `|` or `&`.
17+
#'
18+
#' - `FALSE` to treat missing values as `FALSE`.
19+
#'
20+
#' - `TRUE` to treat missing values as `TRUE`.
21+
#'
22+
#' @param size An optional output size. Only useful to specify if `x` could be
23+
#' an empty list.
24+
#'
25+
#' @param x_arg Argument name used in error messages.
26+
#'
27+
#' @returns A logical vector the same size as the vectors in `x`.
28+
#'
29+
#' @details
30+
#' `list_pany()` and `list_pall()` are consistent with [any()] and [all()] when
31+
#' there are no inputs to process in parallel:
32+
#'
33+
#' - `any()` returns `FALSE` with no inputs. Similarly,
34+
#' `list_pany(list(), size = 1)` returns `FALSE`.
35+
#'
36+
#' - `all()` returns `TRUE` with no inputs. Similarly,
37+
#' `list_pall(list(), size = 1)` returns `TRUE`.
38+
#'
39+
#' @name parallel-operators
40+
#'
41+
#' @examples
42+
#' a <- c(TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, NA, NA, NA)
43+
#' b <- c(TRUE, FALSE, NA, TRUE, FALSE, NA, TRUE, FALSE, NA)
44+
#' x <- list(a, b)
45+
#'
46+
#' # Default behavior treats missings like `|` does
47+
#' list_pany(x)
48+
#' a | b
49+
#'
50+
#' # Default behavior treats missings like `&` does
51+
#' list_pall(x)
52+
#' a & b
53+
#'
54+
#' # Remove missings from the computation, like `na_rm = TRUE`
55+
#' list_pany(x, missing = FALSE)
56+
#' (a & !is.na(a)) | (b & !is.na(b))
57+
#'
58+
#' list_pall(x, missing = TRUE)
59+
#' (a | is.na(a)) & (b | is.na(b))
60+
#'
61+
#' # `list_pall()` can be used to implement a `dplyr::filter()` style API
62+
#' df <- data_frame(id = seq_along(a), a = a, b = b)
63+
#'
64+
#' keep_rows <- function(x, ...) {
65+
#' vec_slice(x, list_pall(list(...), missing = FALSE))
66+
#' }
67+
#' drop_rows <- function(x, ...) {
68+
#' vec_slice(x, !list_pall(list(...), missing = FALSE))
69+
#' }
70+
#'
71+
#' # "Keep / Drop the rows when both a and b are TRUE"
72+
#' # These form complements of one another, even with `NA`s.
73+
#' keep_rows(df, a, b)
74+
#' drop_rows(df, a, b)
75+
#'
76+
#' # Same empty behavior as `any()` and `all()`
77+
#' list_pany(list(), size = 1)
78+
#' any()
79+
#'
80+
#' list_pall(list(), size = 1)
81+
#' all()
82+
NULL
83+
84+
#' @rdname parallel-operators
85+
#' @export
86+
list_pany <- function(
87+
x,
88+
...,
89+
missing = NULL,
90+
size = NULL,
91+
x_arg = caller_arg(x),
92+
error_call = current_env()
93+
) {
94+
check_dots_empty0(...)
95+
.Call(ffi_list_pany, x, missing, size, environment())
96+
}
97+
98+
#' @rdname parallel-operators
99+
#' @export
100+
list_pall <- function(
101+
x,
102+
...,
103+
missing = NULL,
104+
size = NULL,
105+
x_arg = caller_arg(x),
106+
error_call = current_env()
107+
) {
108+
check_dots_empty0(...)
109+
.Call(ffi_list_pall, x, missing, size, environment())
110+
}

_pkgdown.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ reference:
103103
- vec_identify_runs
104104
- vec_expand_grid
105105

106+
- title: Reducers
107+
contents:
108+
- list_pall
109+
106110
- title: New classes
107111
contents:
108112
- list_of

man/parallel-operators.Rd

Lines changed: 109 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/decl/parallel-decl.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
static
2+
r_obj* list_parallel(
3+
r_obj* xs,
4+
enum list_parallel_missing missing,
5+
r_ssize size,
6+
struct vctrs_arg* p_xs_arg,
7+
struct r_lazy error_call,
8+
enum list_parallel_variant parallel
9+
);
10+
11+
static inline
12+
void list_parallel_init(const int* v_x, enum list_parallel_missing missing, r_ssize size, int* v_out);
13+
static inline
14+
void list_parallel_init_missing_as_na(const int* v_x, r_ssize size, int* v_out);
15+
static inline
16+
void list_parallel_init_missing_as_false(const int* v_x, r_ssize size, int* v_out);
17+
static inline
18+
void list_parallel_init_missing_as_true(const int* v_x, r_ssize size, int* v_out);
19+
20+
static inline
21+
void list_pany_fill(const int* v_x, enum list_parallel_missing missing, r_ssize size, int* v_out);
22+
static inline
23+
void list_pall_fill(const int* v_x, enum list_parallel_missing missing, r_ssize size, int* v_out);
24+
static inline
25+
void list_pany_fill_missing_as_na(const int* v_x, r_ssize size, int* v_out);
26+
static inline
27+
void list_pall_fill_missing_as_na(const int* v_x, r_ssize size, int* v_out);
28+
static inline
29+
void list_pany_fill_missing_as_false(const int* v_x, r_ssize size, int* v_out);
30+
static inline
31+
void list_pall_fill_missing_as_false(const int* v_x, r_ssize size, int* v_out);
32+
static inline
33+
void list_pany_fill_missing_as_true(const int* v_x, r_ssize size, int* v_out);
34+
static inline
35+
void list_pall_fill_missing_as_true(const int* v_x, r_ssize size, int* v_out);
36+
37+
static
38+
enum list_parallel_missing parse_list_parallel_missing(r_obj* missing, struct r_lazy error_call);
39+
40+
static
41+
r_ssize compute_size(r_ssize size, r_obj* xs);

src/init.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ extern r_obj* ffi_vec_replace_when(r_obj*, r_obj*, r_obj*, r_obj*);
171171
extern r_obj* ffi_vec_recode_values(r_obj*, r_obj*, r_obj*, r_obj*, r_obj*, r_obj*, r_obj*, r_obj*, r_obj*);
172172
extern r_obj* ffi_vec_replace_values(r_obj*, r_obj*, r_obj*, r_obj*, r_obj*, r_obj*);
173173
extern r_obj* ffi_vec_if_else(r_obj*, r_obj*, r_obj*, r_obj*, r_obj*, r_obj*);
174+
extern r_obj* ffi_list_pany(r_obj*, r_obj*, r_obj*, r_obj*);
175+
extern r_obj* ffi_list_pall(r_obj*, r_obj*, r_obj*, r_obj*);
174176

175177

176178
// Maturing
@@ -372,6 +374,8 @@ static const R_CallMethodDef CallEntries[] = {
372374
{"ffi_vec_recode_values", (DL_FUNC) &ffi_vec_recode_values, 9},
373375
{"ffi_vec_replace_values", (DL_FUNC) &ffi_vec_replace_values, 6},
374376
{"ffi_vec_if_else", (DL_FUNC) &ffi_vec_if_else, 6},
377+
{"ffi_list_pany", (DL_FUNC) &ffi_list_pany, 4},
378+
{"ffi_list_pall", (DL_FUNC) &ffi_list_pall, 4},
375379
{"ffi_exp_vec_cast", (DL_FUNC) &exp_vec_cast, 2},
376380
{NULL, NULL, 0}
377381
};

0 commit comments

Comments
 (0)