There are three relevant differences between the operators &&
/||
and &
/|
, which are explained in the official documentation. Here’s a summary:
1. &
and |
are vectorised
This means that if you want to perform element-wise logical operations on vectors you should use &
and |
:
a = c(TRUE, TRUE, FALSE, FALSE)b = c(TRUE, FALSE, TRUE, FALSE)a | b# [1] TRUE TRUE TRUE FALSEa || b# Error in a || b : 'length = 4' in coercion to 'logical(1)'
In previous versions of R, a || b
(and a && b
) did not cause an error. Instead, the operations silently truncated the output (only the first element was returned; before making this an error it briefly caused a warning in R 4.2).
2. &&
and ||
are short-circuited
Short-circuiting means that the right-hand side of the expression is only evaluated if the left-hand side does not already determine the outcome. Pretty much every programming language does this for conditional operations, since it leads to handy idioms when writing if
conditions, e.g.:
if (length(x) > 0L && x[1L] == 42) …
This code relies on short-circuiting: without it, the code would fail if x
is empty, since the right-hand side attempts to access a non-existent element. Without short-circuiting, we would have to use nested if
blocks, leading to more verbose code:
if (length(x) > 0L) { if (x[1L] == 42) …}
As a general rule, inside a conditional expression (if
, while
) you should always use &&
and ||
, even if short-circuiting isn’t required: it’s more idiomatic, and leads to more uniform code.
3. &
and |
can perform bitwise arithmetic
In many (most?) programming languages, &
and |
actually perform bitwise arithmetic instead of Boolean arithmetic. That is, for two integers a
and b
, a & b
calculates the bitwise and, and a | b
calculates the bitwise or. For Boolean values there’s no difference between bitwise and logical operations; but for arbitrary integers, the result differs. For instance, 1 | 2 == 3
in most programming languages.
However, this is not true for R: R coerces numeric arguments of &
and |
to logical values and performs Boolean arithmetic.
… except when both arguments are of type raw
:
c(1, 3) | c(2, 4)# [1] TRUE TRUEas.raw(c(1, 3)) | as.raw(c(2, 4))# [1] 03 07
It is worth noting that the operations !
(logical negation) and xor
also perform bitwise arithmetic when called with raw
arguments.