Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Ruby "include?" behave differently when nested within an if/end conditional?

To my mind the following three examples are logically equivalent and ought to generate identical results.

print "Enter your string: "
my_string = gets.chomp

#Example 1
my_string.include? ("j" or "J")
puts "1. Your string includes a j or J." 

#Example 2
if
  my_string.include? ("j" or "J")
  puts "2. Your string includes a j or J." 
end

#Example 3
if
  my_string.include? ("j") or my_string.include? ("J")
  puts "3. Your string includes a j or J." 
end

Here are the results with a capital J.

Enter your string: Jack
1. Your string includes a j or J.
3. Your string includes a j or J. 

Here are the results with a lower case j.

Enter your string: jack
1. Your string includes a j or J.
2. Your string includes a j or J.
3. Your string includes a j or J.

For reasons I don't understand, only examples 1 and 3 generate identical results. In example 2, by contrast, only the first item in the brackets (the lower case j) is evaluated.

But why? If example 1 works perfectly, why does example 2 fail simply because the "include?" method is nested within an "if/end"?

And if we need to wrap the code in an "if/elsif/end" conditional, is there a way in which to list multiple values to be assessed in an "include?" method without needing to write them out individually as in example 3? This would be tedious - almost "anti-Ruby" - if there were many values to be evaluated!

In other words, is there a means of making example 2 work correctly?

like image 292
TechnoCat Avatar asked Dec 07 '25 09:12

TechnoCat


1 Answers

Here's what's happening in each of these examples:

First Example

This example outputs 1. Your string includes a j or J. regardless of the previous line. The my_string.include? check is being ignored as it's not used in a comparison anywhere, so the second line is just a regular puts.

Second Example

The second example is a little more interesting. ("j" or "J") is syntax in Ruby which will output the first of the provided arguments which evaluates to true. "j" evaluates to true because it's not nil or false, so it becomes the argument of the second include? method. include? is case-sensitive, so it will return false – the string Jack does not include a lowercase j.

You can try this out by running irb and entering something like 1 or 2 or false and 1; you'll see pretty quickly that the first true argument is returned (or false if no arguments are true).

There's no good way to make this work as-is, other than updating the include? check to use something like set intersections. An easier solution may be to downcase the input before checking characters.

Avdi Grimm posted a good video on using and and or in Ruby.

Third Example

The third example is calling include? twice on the string, and returning true when it hits the second call, hence the if statement being evaluated.

Update

papirtiger's answer got me thinking, so I did a bit of digging with Ripper using the following script:

require 'ripper'
require 'pp'

expression = <<-FOO
if true
  puts 'Hello'
end
FOO

pp Ripper.sexp(expression)

Here's the result:

[:program,
 [[:if,
   [:var_ref, [:@kw, "true", [1, 3]]],
   [[:command,
     [:@ident, "puts", [2, 2]],
     [:args_add_block,
      [[:string_literal,
        [:string_content, [:@tstring_content, "Hello", [2, 8]]]]],
      false]]],
   nil]]]

After updating the expression to the following:

expression = <<-FOO
if
  true
  puts 'Hello'
end
FOO

This was the new output:

[:program,
 [[:if,
   [:var_ref, [:@kw, "true", [2, 2]]],
   [[:command,
     [:@ident, "puts", [3, 2]],
     [:args_add_block,
      [[:string_literal,
        [:string_content, [:@tstring_content, "Hello", [3, 8]]]]],
      false]]],
   nil]]]

It looks as though Ruby does indeed ignore any whitespace and evaluate the next expression. I don't have enough expertise to dig much deeper, but after trying a few more examples (such as throwing a dozen newlines in after an if statement), I'm convinced.

like image 140
Sam Avatar answered Dec 10 '25 01:12

Sam



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!