为什么Ruby“包含”?嵌套在if / end条件中时表现不同?

时间:2015-04-21 16:55:57

标签: ruby-on-rails ruby

在我看来,以下三个例子在逻辑上是等价的,应该产生相同的结果。

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

以下是大写字母J的结果。

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

以下是小写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.

由于我不明白的原因,只有示例1和3生成相同的结果。相反,在示例2中,仅评估括号中的第一项(小写j)。

但为什么呢?如果示例1完美地运行,为什么示例2仅仅因为“包含”而失败?方法嵌套在“if / end”中?

如果我们需要将代码包装在“if / elsif / end”条件中,是否有一种方法可以列出要在“include?”中评估的多个值?方法,不需要像例3那样单独写出来?如果要评估很多值,这将是乏味的 - 几乎是“反Ruby” -

换句话说,有没有办法使示例2正常工作?

4 个答案:

答案 0 :(得分:4)

以下是每个例子中发生的事情:

第一个例子

此示例输出1. Your string includes a j or J.,无论前一行如何。 my_string.include?检查被忽略,因为它未在任何地方进行比较,因此第二行只是常规puts

第二个例子

第二个例子更有趣。 ("j" or "J")是Ruby中的语法,它将输出第一个提供的参数,其值为true"j"求值为true,因为它不是nil或false,所以它成为第二个include?方法的参数。 include?区分大小写,因此会返回false - 字符串Jack不包含小写j

您可以通过运行irb并输入1 or 2false and 1等内容来尝试此操作。你很快就会看到返回第一个真正的参数(如果没有参数,则为false。)

除了更新include?检查以使用类似set intersections之类的内容之外,没有其它方法可以按原样进行此项工作。在检查字符之前,更简单的解决方案可能是downcase输入。

Avdi Grimm posted a good video在Ruby中使用andor

第三个例子

第三个例子是在字符串上调用include?两次,当它到达第二个调用时返回true,因此正在评估if语句。

更新

papirtiger的回答让我思考,所以我使用以下脚本对Ripper进行了一些挖掘:

require 'ripper'
require 'pp'

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

pp Ripper.sexp(expression)

结果如下:

[: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]]]

将表达式更新为以下内容后:

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

这是新的输出:

[: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]]]

看起来Ruby确实忽略了任何空格并评估下一个表达式。我没有足够的专业知识来深入挖掘,但在尝试了一些更多的例子之后(例如在if陈述之后抛出了十几条换行符),我深信不疑。

答案 1 :(得分:3)

Ruby解释器逐行评估所有内容,并在调用方法之前首先计算参数。例如,如果您有一个读取foo(bar('test'))的代码,那么解释器将首先评估'test'将返回字符串test,然后将其作为参数传递给bar并且任何bar返回都将传递给foo

让我们对每个例子应用相同的分析。

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

口译员将在"j" or "J"行评估my_string.include? ("j" or "J")。如果您输入irb "j" or "J",您会看到输出为"j"。这条线变为my_string.include?("j")my_string评估为"Jack",因此整个表达式变为"Jack".include?("j"),返回false,我们不对该信息执行任何操作,我们只是移到下一行并打印一个语句到屏幕{ {1}}。因此,无论您输入什么字符串,您都将从示例1中得到相同的答案。

让我们转到示例2.如果您在puts "1. Your string includes a j or J."之后删除不必要的新行,则可以用更易理解的方式重写它:

if

我们尚未分析#Example 2 if my_string.include?("j" or "J") puts "2. Your string includes a j or J." end ,我们知道它将与my_string.include?("j" or "J")相同,后者将返回false。因此我们得到"Jack".include?("j"),因此if false ...语句中的puts语句将不会被执行,并且不会在屏幕上显示任何内容。

通过删除if

之后的额外新行,可以更清晰地编写最后一个示例
if

它会评估为if my_string.include? ("j") or my_string.include? ("J") puts "3. Your string includes a j or J." end if false or true..,因此它会打印语句。

答案 2 :(得分:1)

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

在这种情况下,"j" or "J"是一个计算结果为"j"的表达式。你可以在irb中运行它来测试你自己。表达式始终使用左右风格的步行进行评估(请参阅http://www.cs.uml.edu/~canning/101/RLWM.pdf)。 Ruby对此有点模糊,因为最后一个方法不需要括号,所以你必须想象它们在那里

因此,"Jack"的输入不会导致此操作,因为它不包含"j"

值得注意的是or运算符与||不同,如果您没有正确使用它,它可能会咬你。

它实际上更像是控制流的Perl-ism。请在此处查看Avdi的截屏视频:http://devblog.avdi.org/2014/08/26/how-to-use-rubys-english-andor-operators-without-going-nuts/

答案 3 :(得分:0)

您可以使用||运算符代替or来解决此问题 - 它们不可互换。例如2,尝试my_string.include?('j') || my_string.include?("J")甚至更好,my_string.downcase.include?('j')