Ruby中使用`begin ... end`作为代码块而没有使用`rescue`,会有意外后果吗?

21

我偶尔会看到Ruby中使用begin...end块而没有在其中加入任何rescueelseensure等语句。例如:

foo = begin
   whatever = 3
   "great"
   42
end
似乎程序员的意图是仅仅利用begin...end代码块的组块属性(就好像begindo一样)。个人认为这种用法有点违反最少惊讶原则(对我来说,begin暗示异常处理)。
在这种方式下使用begin...end是否存在任何意外后果?begin...end代码块是否有任何语义差异(也许在异常处理方面?)会导致此用法危险?
Ruby的语法非常微妙,如果存在奇怪的意外情况,我也不会感到惊讶。

1
@tokland,提供的链接中的代码片段非常清晰。但是,这个人工制造的例子(在问题中)不是很清晰。 :) - Sergio Tulentsev
@tokland:我不是说我现在还不明白它的目的。我是说原始代码片段太荒谬了,让我误入歧途。:) 如果它说“# 在这里想象重计算”,那就更有意义了。 - Sergio Tulentsev
我忘记了我在哪里使用过这个,但是在查看那个页面之后,我记得它对于记忆化特别有用(@cache ||= begin...)。从那时起,您就不必编写 if @cache.nil? ; @cache = ... 了。但无论如何,我认为我的答案应该解决问题。 - mrbrdo
@SergioTulentsev:抱歉,我以为你指的是我的代码片段(那个也不是很好);-) 是的,在begin/end块内使用赋值来计算最后一个值更好。@mrbrdo:是的,@var ||= begin ... end是另一种典型用法;同样,它是一种获得“惰性表达式”的方式(在可以短路的意义下)。 - tokland
@pje: 我相信stackoverflow的规则之一是:“不要动别人的代码”。不管怎样,我认为你现在已经得到了答案 :) - Sergio Tulentsev
显示剩余7条评论
1个回答

37

有时候,我会使用这种方法将一些内容赋值给变量,但是在赋值之前需要先计算出所需的值。这样可以使代码更加简洁。我认为这是用户个人偏好。基本上你是在说:我正在将某些内容赋值给foo变量,但是为了得到我想要的值,我首先需要执行一些操作。当进行记忆化时,这种方法特别有用,因此不需要

if @cache.nil?
  do_something!
  @cache = read_value
end

您可以做

@cache ||= begin
  do_something!
  read_value
end

您在这里利用的是Ruby解释器具有堆栈的特性,每个表达式通常会将某些内容推入堆栈或从堆栈中取出某些内容。赋值只需从堆栈中取出最后一个内容并将其分配(在这种情况下是begin/end的最后一行)。很多时候知道这点(Ruby中的堆栈方法)会很有用。

我不认为它违反了“最小惊讶原则”,我认为使用它与否是用户偏好问题。

您可以通过查看Ruby MRI 1.9中生成的字节码指令来看到它不会产生任何意外的结果:

 RubyVM::InstructionSequence::compile("c = begin; a = 5; 6; end").to_a

 [:trace, 1],
 [:trace, 1],
 [:putobject, 5],
 [:setlocal, 2],
 [:trace, 1],
 [:putobject, 6],
 [:dup],
 [:setlocal, 3],
 [:leave]

Trace只是用于堆栈跟踪,您可以忽略该操作。Dup会复制堆栈上的最后一个项。在此示例中,本地变量a的编号为2,本地变量c的编号为3(因此putobject, 2将分配给变量a,以此类推)。 与a = 5; c = 6相比,唯一的副作用是dup指令,这意味着您的方法的堆栈大小将增加1个插槽。但这并不特别重要,因为它只有在解释器在此特定方法内部时才具有任何效果,并且堆栈的内存已经预留,所以它只是意味着堆栈指针将减少1,而不是其他更多。所以基本上没有任何改变。即使打开了优化,dup也可能会消失。


1
这并没有回答我的问题。 - pje
2
我通常用括号来实现这个目的。 - sawa
我不喜欢多行的方式,但每个人都有自己的个人偏好。 - mrbrdo
你提供的链接并不是这里可能存在问题的原因。虽然编译后的指令序列是特定于MRI1.9的,但MRI是事实上的标准,因此任何不同的行为都是非标准的。而且我很确定所有其他解释器的行为都是相同的。你现在只是挑刺而已。begin/end甚至没有自己的局部变量作用域,所以在你的上下文中完全安全。绝对不会有任何意外的后果,我敢肯定。我对Ruby的内部结构非常了解。你唯一做的就是将赋值给begin/end的最后一行。 - mrbrdo
2
另外,你提供的链接只是语法相关的,与行为无关。请记住,Ruby 没有正式的语言规范。我认为我已经回答了你的问题,或者这个问题无法回答。我不知道你对答案有什么具体期望。 - mrbrdo
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接