我偶尔会看到红宝石中使用的begin...end
块,其间没有任何rescue
,else
,ensure
等语句。例如:
foo = begin
whatever = 3
"great"
42
end
编码人员的意图似乎是仅使用begin...end
块来表示其块分组质量(就好像begin
是do
)。就个人而言,我认为这种用法违反了最少惊喜的原则(begin
意味着对我的异常处理)。
以这种方式使用begin...end
是否会产生任何意外后果? begin...end
块是否存在任何语义差异(可能在异常处理中?),这会使这种使用变得危险吗?
Ruby的语法令人难以置信的微妙,如果在这里等待奇怪的陷阱,我不会感到惊讶。
答案 0 :(得分:33)
我有时会使用它,如果我想为变量赋值,但我必须首先计算我想要分配的值。它使代码更加整洁。我认为这是用户偏好。基本上你说的是:我正在为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复制堆栈中的最后一项。在此示例中,局部变量a
的编号为2
,局部变量c
的编号为3
(因此putobject, 2
将分配给变量{ {1}}等)。
与a
相比,此唯一的副作用是a = 5; c = 6
指令,这意味着您的方法的堆栈大小将增加1个插槽。但这并不是特别重要,因为它只有在解释器在这个特定方法中时才有效,并且堆栈的内存无论如何都是预先保留的,所以它只意味着堆栈指针将比其它方式减少1。所以基本上没有变化。启用优化后,即使dup
可能会消失。