4

I have a quick question :

In ruby, If I write

def test
  foo && a  == b && c == "bar"
end

if foo is null or false, will it keep evaluating the rest of the expression ?

Does it change anything if I do this instead

def test
  a == b && c == "bar" if foo
end

thanks a lot

Sagar Pandya
  • 9,323
  • 2
  • 24
  • 35
Mike W
  • 391
  • 4
  • 14
  • no, every statement chained with `&&` has to be true. If `foo` is nil or false, the rest of the expression will be disregarded. – dimitry_n Nov 20 '16 at 21:07
  • Boolean expression is always evaluated from left to right. If first condition is false and you used &&, then no further evaluation will be executed. – Wasi Ahmad Nov 20 '16 at 21:07
  • ok so both statements are the same in case foo is null or false ? right ? – Mike W Nov 20 '16 at 21:08
  • As soon as an element is `nil` or `false` the expression is determined to be that value, so no other subsequent elements need be examined. Ruby stops processing at this point for two reasons. The first is efficiency: there's no point in wasting CPU cycles by continuing, especially when subsequent operations are expensive. Secondly, and more importantly, it may be that later expressions should only be performed when all the preceding ones are truthy. For example, `today_is_pay_ day? && run_payroll`. Often, as in this example, the logical return value is of no interest. – Cary Swoveland Nov 20 '16 at 22:20
  • There are many ways you could obtain the simple answer ("no") yourself. For example, `a = 0; false && (a += 1) == 1 #=> false; a #=> 0`. That does not answer the "why", however. – Cary Swoveland Nov 21 '16 at 03:34

2 Answers2

6

Theory

&& is a lazy operator, just like ||.

It means that in a && b, if a is false or nil, Ruby won't bother to check b because a && b will be false/nil anyway.

This behaviour is actually desired, because it saves time and could avoid NoMethodErrors.

if a && method_which_requires_a_non_nil_parameter(a)
  # ...
end

method_which_requires_a_non_nil_parameter wouldn't be called at all if a is nil.

or :

x = x || long_method_to_calculate_x

is often used for caching, more often written as :

@x ||= long_method_to_calculate_x

Answer

def test
  foo && a  == b && c == "bar"
end

The rest of the expression won't be evaluated if foo is nil or false : a, b, c could even be undefined without raising a NameError.

def test
  a == b & c == "bar" if foo
end
  • If foo is truthy, the results will be exactly the same.

  • If foo is nil or false, the 2 equalities won't be evaluated, just like with the first example. But :

    • If foo is nil, both test will return nil.

    • If foo is false, first example will return false, second example will return nil.

Eric Duminil
  • 52,989
  • 9
  • 71
  • 124
  • Seems that if I have an expression like `myVar.is_a? String && myVar.empty?` and I set `myVar` to a non-string such as a boolean, the execution will be terminated (`undefined method empty? for false:FalseClass NoMethodError`). Is this still consistent with the idea that `&&` is lazy? – shim Nov 11 '21 at 18:35
  • @shim: That's a Ruby gotcha. Your expression is parsed as `myVar.is_a?(String && myVar.empty?)`. You probably need `myVar.is_a?(String) && myVar.empty?`. `&&` is always lazy, but method precedence can be weird (see https://stackoverflow.com/questions/41313154/whats-the-precedence-of-method-calls-with-and-without-parentheses) – Eric Duminil Nov 11 '21 at 20:37
1

"If foo is null or false, will it keep evaluating the rest of the expression?" No, it will not

This table should help you in such questions:

The following table is ordered according to descending precedence (highest precedence at the top)

N A M  Operator(s)            Description
- - -  -----------            -----------
1 R Y  ! ~ +                  boolean NOT, bitwise complement, unary plus
                              (unary plus may be redefined from Ruby 1.9 with +@)

2 R Y  **                     exponentiation
1 R Y  -                      unary minus (redefine with -@)

2 L Y  * / %                  multiplication, division, modulo (remainder)
2 L Y  + -                    addition (or concatenation), subtraction

2 L Y  << >>                  bitwise shift-left (or append), bitwise shift-right
2 L Y  &                      bitwise AND

2 L Y  | ^                    bitwise OR, bitwise XOR (exclusive OR)
2 L Y  < <= >= >              ordering

2 N Y  == === != =~ !~ <=>    equality, pattern matching, comparison
                              (!= and !~ may not be redefined prior to Ruby 1.9)

2 L N  &&                     boolean AND
2 L N  ||                     boolean OR

2 N N  .. ...                 range creation (inclusive and exclusive)
                              and boolean flip-flops

3 R N  ? :                    ternary if-then-else (conditional)
2 L N  rescue                 exception-handling modifier

2 R N  =                      assignment
2 R N  **= *= /= %= += -=     assignment
2 R N  <<= >>=                assignment
2 R N  &&= &= ||= |= ^=       assignment

1 N N  defined?               test variable definition and type
1 R N  not                    boolean NOT (low precedence)
2 L N  and or                 boolean AND, boolean OR (low precedence)
2 N N  if unless while until  conditional and loop modifiers
P.S.
  • 15,970
  • 14
  • 62
  • 86