rboolean-expressionbooleanquery

In R programming, what's the difference between & vs &&, and | vs ||


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

Solution

  • 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!

    Edit:

    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!