I know the part that && and || make for more efficient code because they do the least number of tests in a chain and break out as soon as we have enough information to know what the result of the chain is going to be.
But I read in Garrett Grolemund's book that "...double operators are not appropriate everywhere. && and || are not vectorized, which means they can only handle a single logical test on each side of the operator..." Can somebody please explain to me what the emphasized part means?
Did some simple tests and while & is doing a element-wise comparison between the corresponding elements of the two logical vectors, && is comparing only the first elements and returning TRUE because the operator is not vecorized? Is that all the emphasized part above means, or is there more to it?
c(T, F, F, F, F) & c(T, T, F, T, F)
[1] TRUE FALSE FALSE FALSE FALSE
c(T, F, F, F, F) && c(T, T, F, T, F)
[1] TRUE
c(F, F, F, F, F) && c(T, T, F, T, F)
[1] FALSE
Collapsing vectors on either side of the operator to one boolean value using any
and all
.
any(T, T, F, F, T) && all(F, F, T, T, T)
[1] FALSE
any(T, T, F, F, T) && any(F, F, T, T, T)
[1] TRUE
they can only handle a single logical test on each side of the operator
a <- c(T, F, F, F)
b <- c(T, F, F, F)
a && b
Returns [1] TRUE
Because only the first element of a
and b
are tested!
Consider the following, where we 'rotate' a
and b
after each &&
test:
a <- c(T, F, T, F)
b <- c(T, F, F, T)
for (i in seq_along(a)){
cat(paste0("'a' is: ", paste0(a, collapse=", "), " and\n'b' is: ", paste0(b, collapse=", "),"\n"))
print(paste0("'a && b' is: ", a && b))
a <- c(a[2:length(a)], a[1])
b <- c(b[2:length(b)], b[i])
}
Gives us:
'a' is: TRUE, FALSE, TRUE, FALSE and
'b' is: TRUE, FALSE, FALSE, TRUE
[1] "'a && b' is: TRUE"
'a' is: FALSE, TRUE, FALSE, TRUE and
'b' is: FALSE, FALSE, TRUE, TRUE
[1] "'a && b' is: FALSE"
'a' is: TRUE, FALSE, TRUE, FALSE and
'b' is: FALSE, TRUE, TRUE, FALSE
[1] "'a && b' is: FALSE"
'a' is: FALSE, TRUE, FALSE, TRUE and
'b' is: TRUE, TRUE, FALSE, TRUE
[1] "'a && b' is: FALSE"
Additionally, &&
, ||
stops as soon as the expression is clear:
FALSE & a_not_existing_object
TRUE | a_not_existing_object
Returns:
Error: object 'a_not_existing_object' not found
Error: object 'a_not_existing_object' not found
But:
FALSE && a_not_existing_object
TRUE || a_not_existing_object
Returns:
[1] FALSE
[1] TRUE
Because anything after FALSE
AND something (and TRUE
OR something) becomes FALSE
and TRUE
respectively
This last behavior of &&
and ||
is especially useful if you want to check in your control-flow for an element that may not exist:
if (exists(a_not_existing_object) && a_not_existing_object > 42) {...}
This way the evaluation stops after the first expression evaluates to FALSE
and the a_not_existing_object > 42
part is not even atempted!