似乎双拼 block 参数在传递的对象上调用 to_ary
,而 lambda 参数和方法参数不会发生这种情况。确认如下。
首先,我准备了一个对象obj
,在该对象上定义了一个方法to_ary
,它返回数组以外的东西(即字符串)。
obj = Object.new
def obj.to_ary; "baz" end
然后,我将这个 obj
传递给具有双 splatted 参数的各种结构:
instance_exec(obj){|**foo|}
# >> TypeError: can't convert Object to Array (Object#to_ary gives String)
->(**foo){}.call(obj)
# >> ArgumentError: wrong number of arguments (given 1, expected 0)
def bar(**foo); end; bar(obj)
# >> ArgumentError: wrong number of arguments (given 1, expected 0)
从上面可以看出,只有代码块会尝试通过调用(可能的)to_ary
方法将 obj
转换为数组。
为什么代码块的双拼参数与 lambda 表达式或方法定义的参数表现不同?
最佳答案
我没有您问题的完整答案,但我会分享我的发现。
精简版
Procs 允许使用与签名中定义的参数数量不同的参数进行调用。如果参数列表与定义不匹配,则调用 #to_ary
进行隐式转换。 Lambda 和方法需要与其签名匹配的参数数量。不执行任何转换,这就是 #to_ary
未被调用的原因。
长版
您所描述的是 lambdas(和方法)和 procs(和 block )处理参数之间的区别。看看这个例子:
obj = Object.new
def obj.to_ary; "baz" end
lambda{|**foo| print foo}.call(obj)
# >> ArgumentError: wrong number of arguments (given 1, expected 0)
proc{|**foo| print foo}.call(obj)
# >> TypeError: can't convert Object to Array (Object#to_ary gives String)
Proc
不需要与其定义的相同数量的参数,并且调用了 #to_ary
(您可能知道):
For procs created using
lambda
or->()
, an error is generated if wrong number of parameters are passed to the proc. For procs created usingProc.new
orKernel.proc
, extra parameters are silently discarded and missing parameters are set tonil
. (Docs)
此外,Proc
调整传递的参数以适应签名:
proc{|head, *tail| print head; print tail}.call([1,2,3])
# >> 1[2, 3]=> nil
来源:makandra , SO question .
#to_ary
用于此调整(这是合理的,因为 #to_ary
用于隐式转换):
obj2 = Class.new{def to_ary; [1,2,3]; end}.new
proc{|head, *tail| print head; print tail}.call(obj2)
# >> 1[2, 3]=> nil
在a ruby tracker中有详细描述.
您可以看到 [1,2,3]
被拆分为 head=1
和 tail=[2,3]
。这与多重赋值中的行为相同:
head, *tail = [1, 2, 3]
# => [1, 2, 3]
tail
# => [2, 3]
正如您所注意到的,#to_ary
在 proc 具有双拼关键字参数时也会被调用:
proc{|head, **tail| print head; print tail}.call(obj2)
# >> 1{}=> nil
proc{|**tail| print tail}.call(obj2)
# >> {}=> nil
在第一种情况下,obj2.to_ary
返回的数组 [1, 2, 3]
被拆分为 head=1
和空尾,因为 **tail
无法匹配 [2, 3]
的数组。
Lambda 和方法没有这种行为。他们需要严格的参数数量。没有隐式转换,所以 #to_ary
没有被调用。
我认为这个区别是在these two lines中实现的Ruby 源代码:
opt_pc = vm_yield_setup_args(ec, iseq, argc, sp, passed_block_handler,
(is_lambda ? arg_setup_method : arg_setup_block));
在 this function 中.我猜 #to_ary
在 vm_callee_setup_block_arg_arg0_splat 的某处被调用,很可能在 RARRAY_AREF
中。我很想阅读这段代码的评论,以了解其中发生的事情。
关于ruby - 为什么从代码块中的双拼参数调用 `to_ary`?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57163086/