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 FALSE
a || b
# [1] TRUE
For &&
/||
all elements after the first are discarded. Recent versions of R generate a helpful warning when using &&
/||
on vectors longer than 1:
In a || b : 'length(x) = 4 > 1' in coercion to 'logical(1)'
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 TRUE
as.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.