instance_eval可能是一个咖喱过程吗?

Joh*_*ter 8 ruby currying

假设我有一个这样的类:

class Test
  def test_func
    140
  end
end
Run Code Online (Sandbox Code Playgroud)

和一个proc,它引用了一个成员函数Test:

p = ->(x, y) { x + y + test_func }  # => #<Proc:0x007fb3143e7f78@(pry):6 (lambda)>
Run Code Online (Sandbox Code Playgroud)

要调用p,我将它绑定到以下实例Test:

test = Test.new                     # => #<Test:0x007fb3143c5a68>
test.instance_exec(1, 2, &p)        # => 143
Run Code Online (Sandbox Code Playgroud)

现在假设我想传递yp,并且总是传递x = 1:

curried = p.curry[1]                # => #<Proc:0x007fb3142be070 (lambda)>
Run Code Online (Sandbox Code Playgroud)

理想情况下,我应该instance_exec像以前一样,但相反:

test.instance_exec(2, &curried)

=> NameError: undefined local variable or method `test_func' for main:Object
Run Code Online (Sandbox Code Playgroud)

proc在似乎不正确的绑定中运行.是什么赋予了?

sim*_*nwo 5

是的,我相信这是一个错误。

我认为这归结为curry返回“C 级过程”而不是正常过程的事实。我不完全理解两者之间的区别(我猜前者是由 Ruby C 代码创建的,这就是curry它的作用),但是当您尝试进行绑定时,您可以看出它们是不同的。

p.binding # => #<Binding:0x000000020b4238>
curried.binding # => ArgumentError: Can't create a binding from C level Proc
Run Code Online (Sandbox Code Playgroud)

通过查看source,这看起来像它们的内部结构表示对于iseq成员具有不同的值,这表示该块包含什么样的指令序列。

这在您调用 时很重要instance_exec,它最终会invoke_block_from_cvm.c 中调用,它根据iseq类型进行分支:

else if (BUILTIN_TYPE(block->iseq) != T_NODE) {
    ...
} else {
    return vm_yield_with_cfunc(th, block, self, argc, argv, blockptr);
}
Run Code Online (Sandbox Code Playgroud)

我错过的分支 ( ...) 最终vm_push_frame以看起来像一些环境的方式调用,而vm_yield_with_cfunc实际上却没有。

所以我的猜测是,因为 curried proc 是在 C 代码中创建的,并且最终具有与您的第一个 proc 不同的“类型”,所以在上面的代码段中采用了另一个分支,并且没有使用环境。

我要指出的是,这一切是相当投机基于阅读的代码,我没有运行任何测试或尝试新鲜事物出来(我也不会全部是熟悉内部红宝石反正!)