我有以下代码:
def call_block
Proc.new.call
my_local_proc = Proc.new { Proc.new.call }
my_local_proc.call
end
call_block { p 'block' }
输出为:
block
block
有人可以向我解释一下 Proc.new 如何找到我传递给 call_block 的 block 吗? 我猜 Proc.new 只是搜索最近的 block ,并且它完全用 C++ 实现。
我还有一个问题:仅使用 ruby 就能实现这样的事情吗?我是说, 我可以编写一个方法,如果没有给出 block ,则采用传递给调用它的方法的 block 。像这样的东西:
def bar
if not block_given?
#use the block that has been given to the caller
end
# some code
end
def foo
bar
end
foo { :block }
最佳答案
Proc.new
如果在附加了一个方法的方法内部没有调用该方法的 block ,则将使用该方法的 block 。 This is documented behavior .
要了解 YARV 是如何实现的,让我们阅读源代码。具体来说, proc_new
function :
block_pointer = rb_vm_control_frame_block_ptr(control_frame_pointer);
该行检索指向与当前控制帧关联的 block 的指针。
我相信这些控制框架实现了 Ruby 的堆栈。我们目前位于Proc.new
内控制框架,因此这将检索指向给定方法的 block 的指针。
if (block_pointer != NULL) {
/* block found */
} else {
/* block not found... */
}
如果指针不是 NULL
,然后Proc.new
显式地传递了一个 block 。如果指针是NULL
怎么办? ,但是?
/* block not found... */
control_frame_pointer = RUBY_VM_PREVIOUS_CONTROL_FRAME(control_frame_pointer);
block_pointer = rb_vm_control_frame_block_ptr(control_frame_pointer);
我们在堆栈上向上移动并尝试获取其 block 。换句话说,我们向上移动到调用者的控制框架并尝试获取其 block 。
if (block_pointer != NULL) {
if (is_lambda) {
rb_warn("tried to create Proc object without a block");
}
} else {
rb_raise(rb_eArgError, "tried to create Proc object without a block");
}
现在,如果不是 NULL
,那么我们就基本成功了。如果仍然 NULL
,那么我们就无法创建 Proc
,所以我们提出 ArgumentError
.
该算法归结为:
- 看看是否
Proc.new
被给予了一个区 block- 如果是这样,请使用它
- 如果没有,查看调用者是否被阻止
- 如果是这样,请使用它
- 如果没有,则引发错误
为了可读性而修改了源代码。访问 GitHub 上的链接源文件以获取原始文件。
关于ruby - Proc.new 如何找到这段代码中的 block ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13672174/