Search code examples
rmatharithmetic-expressions

R- arithmetic does not respect logical NOT operator and order of operations?


It appears that the logical NOT operator ! has non-intuitive order of operations in arithemtic:

set.seed(42)
a  <-  sample(100, 3)
b  <-  sample(100, 3)
c  <-  sample(100, 3)
l  <-  (1:3) <= 2

a * !l - b * !l + c
# 0  0 29

# same expression, but with parentheses for explicit grouping order of operations
(a * !l) - (b * !l) + c
# 74 14 43

There must be something I do not understand about the ! operator in relation to * or conversion from logical to numeric?


Solution

  • Note that in R, the negation operator ! will apply to entire expression to the right of the operator until it gets to the end or encounters an expression with a lower precedence. It does not just negate the most immediate term. Recall also that 0 is treated as FALSE and any other number is TRUE. So observe

    !0
    # [1] TRUE
    !5
    # [1] FALSE
    !5-5
    # [1] TRUE
    !5-3-2
    # [1] TRUE
    (!5)-3-2
    # [1] -5
    

    So you see in the case of !5-3-2 the negation isn't happening until after the 5-3-2 is evaluated. Without the parenthesis, the negation is the very last thing that happens.

    So when you write

    a * !l - b * !l + c
    

    that's the same as

    a * !(l - (b * !(l + c)))
    

    Because all the operations have to happen to the right of the negation before the negation can occur.

    If you want to negate just the l terms, you can do

    a * (!l) - b * (!l) + c
    

    This is a function of the operator precedence in R (see the ?Syntax help page for details). It's once of the last operators to be evaluated in the given expression.

    Note that & and | have a lower precedence than ! so when you do

    !a | !b & !c
    

    that's the same as

    (!a) | ((!b) & (!c))
    

    so this roughly would be what you expect if you just stick to logical operators. It just gets a bit odd perhaps when you combine logical and arithmetic operators.