有时候,我会使用这种方法将一些内容赋值给变量,但是在赋值之前需要先计算出所需的值。这样可以使代码更加简洁。我认为这是用户个人偏好。基本上你是在说:我正在将某些内容赋值给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
也可能会消失。
@var ||= begin ... end
是另一种典型用法;同样,它是一种获得“惰性表达式”的方式(在可以短路的意义下)。 - tokland