LazyAnd: Lazy (short-circuited), vectorized &/&& and |/||

Description Usage Arguments Value Warning Examples

View source: R/Misc.R

Description

In base R, & is vectorized, and && is short-circuited (also called lazy evaluation). LazyAnd combines these two: a is evaluated, and then fun(b) is called for the positions where a evaluates to TRUE (or NA), to decide a & b. The advantage is that it's possible to check for valid inputs and use these inputs in one go, or that it's possible to only call an expensive function when needed.

LazyOr is analogous, fun(b) only called if a is FALSE or NA

Usage

1
2
3
LazyAnd(a, b, fun, ...)

LazyOr(a, b, fun, ...)

Arguments

a

A logical

b

A vector equal to length of a (unless length(a)==1), which is used if a evaluates to TRUE or NA (for LazyAnd), or if a evaluates to FALSE or NA (for LazyOr)

fun

Function to be called. Note that this function needs fun(b[x]) to be independent of fun(b[y])

...

Additional arguments to be passed on to fun

Value

LazyAnd: a & fun(b), but fun(b) is only called for those b[x] where a[x]==TRUE || is.na(a[x])

LazyOr: a | fun(b), but fun(b) is only called for those b[x] where a[x]==FALSE || is.na(a[x])

Warning

Note that this may produce unexpected results if elements of fun(b) are not independent of each other, e.g. calling:
nums <- 1:10 nums %% 2==0 & cumsum(nums) %% 2==0

and

LazyAnd(nums %% 2==0, nums, function(x) {cumsum(x) %% 2==0})

gives different results, as cumsum(1:10[c(2,4,6,8,10)]) is called, instead of cumsum(1:10)[c(2,4,6,8,10)], which produces different results

Examples

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# A function to check evenness, but who prints an alert if the value is more then 10
input <- data.frame(valid=c(TRUE,TRUE,TRUE,TRUE,FALSE,FALSE),
value=c('1','12',2,'3','huh',14), stringsAsFactors = FALSE)
fun <- function(x) {
  if(any(as.numeric(x)>10))
    cat('Numbers over 10 (unexpected):',as.numeric(x)[as.numeric(x)>10], '')
  return(as.numeric(x) %% 2==0)
}
cat("\nAnd in total we have",sum(input$valid & fun(input$value)),"even numbers")
cat("\nWith LazyAnd we have in total:",sum(LazyAnd(input$valid, input$value, fun)),"even numbers")

# Example where calling a function for all possible values would be possible,
# but (prohibitively) expensive
set.seed(4)
# This function may be very expensive, so we don't want to check all numbers
is.prime <- function(n) n == 2L || all(n %% 2L:max(2,floor(sqrt(n))) != 0)
n <- floor(runif(1e4, min=0, max=.5)^(-4))
surely_prime <- LazyAnd(n<1e10, n, sapply, FUN=is.prime)

# The difference between this call and
## Not run: sapply(n, is.prime) # Don't try this at home!
# is getting results for 62 more occurences (with low probability of being prime,
# probably 2-3 of them are prime), at the cost of a LOT of resources.

EmilBode/EmilMisc documentation built on Feb. 24, 2020, 4:11 p.m.