看起来,双溅块参数在传递的对象上调用to_ary,这与lambda参数和方法参数无关。这一点得到了如下证实。
首先,我准备了一个对象obj,其中定义了一个方法to_ary,它返回的不是数组(即字符串)。
obj = Object.new
def obj.to_ary; "baz" end然后,我将这个obj传递给具有双溅参数的各种构造:
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)如上所述,只有代码块尝试通过调用(可能的) obj方法将to_ary转换为数组。
为什么代码块的双溅参数行为与lambda表达式或方法定义的参数不同?
发布于 2019-07-24 21:22:27
我对你的问题没有完整的答案,但我会分享我的发现。
短版
Procs允许使用与签名中定义的参数数目不同的参数进行调用。如果参数列表与定义不匹配,则调用#to_ary进行隐式转换。Lambdas和方法需要匹配它们的签名的args数。不执行转换,这就是不调用#to_ary的原因。
长版
您所描述的是由lambdas (和方法)处理params和procs (和块)之间的区别。看一下这个例子:
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不需要它定义的相同数量的args,并且调用#to_ary (正如您可能知道的那样):
对于使用
lambda或->()创建的proc,如果将错误的参数数传递给proc,则会生成错误。对于使用Proc.new或Kernel.proc创建的procs,额外的参数将被静默丢弃,缺少的参数被设置为nil。(文档)
更重要的是,Proc调整传递的参数以适应签名:
proc{|head, *tail| print head; print tail}.call([1,2,3])
# >> 1[2, 3]=> nil#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它在红宝石追踪器中有详细的描述。
您可以看到,[1,2,3]被拆分为head=1和tail=[2,3]。这和多任务中的行为是一样的:
head, *tail = [1, 2, 3]
# => [1, 2, 3]
tail
# => [2, 3]正如您已经注意到的,当proc有双溅关键字args时,也会调用#to_ary:
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]数组。
Lambdas和方法没有这种行为。它们需要严格数量的对撞机。没有隐式转换,因此不调用#to_ary。
我认为这种差异是在Ruby的这两条线中实现的:
opt_pc = vm_yield_setup_args(ec, iseq, argc, sp, passed_block_handler,
(is_lambda ? arg_setup_method : arg_setup_block));在这一功能。我猜#to_ary在飞溅的某个地方被称为,很可能在RARRAY_AREF中。我很乐意阅读这段代码的评论,以了解里面发生了什么。
https://stackoverflow.com/questions/57163086
复制相似问题