Ruby Block语句和隐式返回

时间:2013-03-03 15:36:32

标签: ruby return block

我一直认为rubyists因为样式偏好而选择在ruby中隐含返回(更少的单词=更简洁)。但是,有人可以向我确认在下面的示例中,您实际上必须隐式返回,否则预期的功能将无效吗? (预期的功能是能够将一个句子分成单词,并为每个单词返回“用元音开始”或“用辅音开始”)

# With Implicit Returns
def begins_with_vowel_or_consonant(words)
  words_array = words.split(" ").map do |word|
    if "aeiou".include?(word[0,1])
      "Begins with a vowel" # => This is an implicit return
    else
      "Begins with a consonant" # => This is another implicit return
    end
  end
end

# With Explicit Returns
def begins_with_vowel_or_consonant(words)
  words_array = words.split(" ").map do |word|
    if "aeiou".include?(word[0,1])
      return "Begins with a vowel" # => This is an explicit return
    else
      return "Begins with a consonant" # => This is another explicit return
    end
  end
end

现在,我知道肯定有很多方法可以使这些代码更高效,更好,但我这样做的原因是为了说明隐含返回的必要性。有人可以向我证实,确实需要隐含的回报,而不仅仅是一种风格选择吗?

编辑: 这是一个例子来说明我想要展示的内容:

# Implicit Return
begins_with_vowel_or_consonant("hello world") # => ["Begins with a consonant", "Begins with a consonant"] 

# Explicit Return
begins_with_vowel_or_consonant("hello world") # => "Begins with a consonant" 

3 个答案:

答案 0 :(得分:5)

方法的隐式返回值是方法中计算的最后一个表达式。

在您的情况下,您注释的两行中的 都不是最后一个表达式。得到评估的最后一个表达式是对words_array的赋值(BTW完全没用,因为它是最后一个表达式,之后无法使用该变量)。

现在,赋值表达式的值是多少?在此特定情况下,它是指定的值map方法的返回值,即Array。所以,这就是方法返回的内容。

second 示例中,在map的第一次迭代中,您将点击两个return中的一个,从而立即从该方法返回。但是,在第一个示例中,您将始终遍历整个words数组。

问题是隐式和显式返回是不同的,问题是你声称隐含返回的两行不是。

答案 1 :(得分:1)

这种情况发生的原因是因为return语句在块内。如果return语句只是一个函数内部,那么执行流程就像你期望的那样。 ruby中的块和返回(以及关于此问题的break语句)是一个奇怪的野兽。让我们用一个简单的例子来捕捉你的要求:

def no_return()
  (1..10).each do |i|
    puts i
  end
  puts 'end'
end

def yes_return()
  (1..10).each do |i|
    puts i
    return 
  end
  puts 'end'
end

如果你同时运行这两个,你会看到第一个将打印数字1-10和单词'end',如你所料,但第二个函数只打印出1.这是因为ruby内部将returns(和break语句)实现为异常。抛出这些异常时,会使用一个名为“catch table”的查找表来尝试查找执行流程应该继续的位置。如果没有找到,ruby内部将搜索rb_control_frame结构的堆栈,寻找指向我们正在执行的代码的指针。因此,在您的情况下,抛出异常并且最接近的指针(就堆栈帧而言)位于方法的末尾,从而导致整个方法的终止。这就是为什么你甚至不会看到'结束'的印刷。

no_return说明:

0000 trace            1                                               (   3)
0002 putnil           
0003 getdynamic       i, 0
0006 send             :puts, 1, nil, 8, <ic:0>
0012 leave    

yes_return说明:

0000 trace            1                                               (   3)
0002 putnil           
0003 getdynamic       i, 0
0006 send             :puts, 1, nil, 8, <ic:0>
0012 pop              
0013 trace            1                                               (   4)
0015 putnil           
0016 throw            1
0018 leave 

重要的一点是在yes_return指令中,有'throw 1'实际上是一个实现为抛出(和捕获)异常的return语句。以相同的方式打破工作,除了他们'扔2'。

要查看所有框架和捕获表的实际说明,请查看我不久前放在一起的应用程序: http://rubybytes.herokuapp.com/

有关更多信息,请查看Pat Shaughnessy的博客,特别是此条目以获取更多信息: http://patshaughnessy.net/2012/6/29/how-ruby-executes-your-code

另一个无耻的插件,如果你是java,请查看我的java版本的rubybytes at javabytes.herokuapp.com查看反汇编的java字节码(对不起它是无链接的,你必须将url复制并粘贴到你的浏览器中)。

答案 2 :(得分:0)

return将从产生于块的方法返回,因此您需要隐式或使用next