05 - Logical Functions
These examples were adapted from Vaughan, Hester, and Francois (2024).
Note
These functions ignore NA
values for now. Adjustments
for handling NA
values are covered in a separate
vignette.
R already provides efficient versions of the functions covered here. This is just to illustrate how to use C++ code.
Is any value in a vector ‘true’?
Base R’s any()
function returns TRUE
if
there is at least one TRUE
element in a vector, and
FALSE
otherwise. Below is one possible C++
implementation:
[[cpp4r::register]]
bool any_cpp(logicals x) {
int n = x.size();
for (int i = 0; i < n; ++i) {
if (x[i]) {
return true;
}
}
return false;
}
Its R equivalent would be:
any_r <- function(x) {
n <- length(x)
for (i in 1:n) {
if (x[i]) {
return(TRUE)
}
}
FALSE
}
Add and document the functions, update the package as in the previous vignettes, and then compare the functions speed with:
# install.packages("bench")
library(bench)
set.seed(123) # for reproducibility
x <- rpois(1e6, lambda = 2) # 1,000,000 elements
y <- ifelse(x > 2, TRUE, FALSE)
any(y)
any_cpp(y)
any_r(y)
mark(
any(y),
any_cpp(y),
any_r(y)
)
Which elements in a vector are ‘true’?
Base R’s which()
function returns the indices of the
TRUE
elements in a vector. Here is a possible C++
implementation:
[[cpp4r::register]]
integers which_cpp(logicals x) {
int n = x.size();
writable::integers res;
int j = 0;
for (int i = 0; i < n; ++i) {
if (x[i]) {
++j;
res.push_back(i + 1);
}
}
if (j == 0) {
return integers(0);
} else {
return res;
}
}
Its R equivalent would be:
which_r <- function(x) {
n <- length(x)
res <- c()
j <- 0
for (i in 1:n) {
if (x[i]) {
res <- c(res, i)
j <- j + 1
}
}
if (j == 0) {
return(0)
} else {
return(res)
}
}
To test the functions, you can run the following benchmark code in the R console:
which(y[1:100])
which_cpp(y[1:100])
which_r(y[1:100])
mark(
which(y[1:1000]),
which_cpp(y[1:1000]),
which_r(y[1:1000])
)
Are all values in a vector ‘true’?
Base R’s all()
function checks if all elements in a
vector are TRUE
. Here is a possible C++ implementation that
loops over the vector:
[[cpp4r::register]]
bool all_cpp_1(logicals x) {
int n = x.size();
for (int i = 0; i < n; ++i) {
if (!x[i]) {
return false;
}
}
return true;
}
More concise C++ alternatives are:
[[cpp4r::register]]
bool all_cpp_2(logicals x) {
for (int i = 0; i < x.size(); ++i) {
if (!x[i]) {
return false;
}
}
return true;
}
[[cpp4r::register]] bool
all_cpp_3(logicals x) {
for (bool i : x) {
if (!i) {
return false;
}
}
return true;
}
[[cpp4r::register]] bool
all_cpp_4(logicals x) {
return std::all_of(x.begin(), x.end(), [](bool x) { return x; });
}
To test the functions, you can run the following tests and benchmark code in the R console:
set.seed(123) # for reproducibility
x <- rpois(1e6, lambda = 2) # 1,000,000 elements
all(x > 2)
all_cpp_1(x > 2)
all_cpp_2(x > 2)
all_cpp_3(x > 2)
all_cpp_4(x > 2)
# also test the TRUE-only case
all(x >= 0)
all_cpp_1(x >= 0)
all_cpp_2(x >= 0)
all_cpp_3(x >= 0)
all_cpp_4(x >= 0)
mark(
all(x > 2),
all_cpp_1(x > 2),
all_cpp_2(x > 2),
all_cpp_3(x > 2),
all_cpp_4(x > 2)
)