21

我偶尔会看到begin...end在 ruby​​ 中使用的块之间没有任何rescue, else,ensure等语句。例如:

foo = begin
   whatever = 3
   "great"
   42
end

看来,编码员的意图是begin...end仅将块用于其块分组质量(好像begindo)。我个人认为这种用法违反了最小意外原则(begin对我来说意味着异常处理)。

以这种方式使用是否有任何意想不到的后果begin...end?块是否begin...end有任何语义差异(可能在异常处理中?)使这种用法很危险?

Ruby 的语法非常微妙,如果这里有奇怪的陷阱等着我,我也不会感到惊讶。

4

1 回答 1

37

如果我想为变量分配一些东西,我有时会使用它,但我必须先计算我想分配的值。这样可以使代码更加整洁。我认为这是用户偏好。基本上你是在说:我正在为 foo 分配一些东西,但是为了获得我想要的值,我首先需要做一些事情。它在做记忆时特别有用,所以代替

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

你可以做

@cache ||= begin
  do_something!
  read_value
end

您在这里利用的是 Ruby 解释器有一个堆栈,每个表达式通常会将某些内容推送到堆栈上,或者从堆栈中获取某些内容。赋值只是从堆栈中取出最后一件事并分配它(在这种情况下,从开始/结束的最后一行)。很多时候知道这一点(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 复制堆栈上的最后一项。在这个例子中,局部变量的编号是,局部变量a2编号c3(因此putobject, 2将分配给变量a等)。与此相比,唯一的副作用a = 5; c = 6dup指令,这意味着您的方法的堆栈大小将大 1 个插槽。但这并不是特别重要,因为它仅在解释器在此特定方法中时才有效,并且堆栈的内存无论如何都是预先保留的,因此它仅意味着堆栈指针将比其他情况多减 1。所以基本上没有任何变化。随着优化的开启,甚至dup可能会消失。

于 2012-11-07T22:23:44.710 回答