2

我一直认为 ruby​​ 学家选择在 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" 
4

3 回答 3

5

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

在您的情况下,您注释的行都不是最后一个表达式。最后一个被求值的表达式是赋值words_array(顺便说一句,它完全没用,因为它是最后一个表达式,之后无法使用该变量)。

现在,赋值表达式的值是多少?它是被分配的值,在这种特殊情况下,map方法的返回值是一个Array. 所以,这就是方法返回的内容。

第二个示例中,在 的第一次迭代中map,您将命中两个returns 中的一个,因此立即从该方法返回。但是,在第一个示例中,您将始终遍历整个words数组。

问题在于隐式和显式返回不同,问题在于您声称是隐式返回的两行不是。

于 2013-03-03T16:11:28.680 回答
1

发生这种情况的原因是因为 return 语句位于块内。如果 return 语句仅在函数内部,则执行流程将如您所愿。ruby 中的块和返回(以及与此相关的中断语句)是一种奇怪的野兽。让我们举一个更简单的例子来捕捉你的要求:

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​​ 内部实现了返回 (和 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 语句。Breaks 以相同的方式工作,除了它们“抛出 2”。

要亲自查看所有框架和捕获表的实际说明,请查看我不久前整理的这个应用程序: http ://rubybytes.herokuapp.com/

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

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

于 2013-03-03T16:49:28.130 回答
0

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

于 2013-03-03T16:25:45.123 回答