saw*_*awa 3 ruby parameters lambda codeblocks
似乎,双精度的块参数调用to_ary了传递的对象,而lambda参数和方法参数则不会发生这种情况。确认如下。
首先,我准备了一个obj在其to_ary上定义方法的对象,该对象返回除数组(即字符串)之外的其他内容。
obj = Object.new
def obj.to_ary; "baz" end
Run Code Online (Sandbox Code Playgroud)
然后,将其传递obj给具有双斜线参数的各种构造:
instance_exec(obj){|**foo|}
# >> TypeError: can't convert Object to Array (Object#to_ary gives String)
Run Code Online (Sandbox Code Playgroud)
->(**foo){}.call(obj)
# >> ArgumentError: wrong number of arguments (given 1, expected 0)
Run Code Online (Sandbox Code Playgroud)
def bar(**foo); end; bar(obj)
# >> ArgumentError: wrong number of arguments (given 1, expected 0)
Run Code Online (Sandbox Code Playgroud)
从上面可以看到,只有代码块尝试obj通过调用(潜在)to_ary方法转换为数组。
为什么代码块的双斜线参数的行为与lambda表达式或方法定义的行为不同?
对于您的问题,我没有完整的答案,但我会分享我发现的内容。
可以使用与签名中定义的参数数量不同的参数来调用Procs。如果参数列表与定义不匹配,#to_ary则调用以进行隐式转换。Lambda和方法需要与它们的签名匹配的多个arg。没有执行任何转换,因此#to_ary不被调用。
您所描述的是通过lambda(和方法)和proc(和块)处理参数之间的区别。看一下这个例子:
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)
Run Code Online (Sandbox Code Playgroud)
Proc不需要与定义的数目相同的args,而是#to_ary被调用(您可能知道):
对于使用
lambda或创建->()的proc,如果将错误数量的参数传递给proc,则会生成错误。对于使用Proc.new或创建的procKernel.proc,静默丢弃多余的参数,并将缺少的参数设置为nil。(文档)
此外,Proc调整传递的参数以适合签名:
proc{|head, *tail| print head; print tail}.call([1,2,3])
# >> 1[2, 3]=> nil
Run Code Online (Sandbox Code Playgroud)
#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
Run Code Online (Sandbox Code Playgroud)
在ruby跟踪器中对其进行了详细描述。
您可以看到将[1,2,3]其拆分为head=1和tail=[2,3]。这与多重分配中的行为相同:
head, *tail = [1, 2, 3]
# => [1, 2, 3]
tail
# => [2, 3]
Run Code Online (Sandbox Code Playgroud)
如您#to_ary所见,当proc具有双斜尾关键字args时,也会调用:
proc{|head, **tail| print head; print tail}.call(obj2)
# >> 1{}=> nil
proc{|**tail| print tail}.call(obj2)
# >> {}=> nil
Run Code Online (Sandbox Code Playgroud)
在第一种情况下,[1, 2, 3]return by 的数组obj2.to_ary被拆分为head=1and空尾,因为**tail无法匹配的数组[2, 3]。
Lambda和方法没有这种行为。他们需要严格的参数数量。没有隐式转换,因此#to_ary不被调用。
我认为这种区别是在Ruby的以下两行中实现的:
opt_pc = vm_yield_setup_args(ec, iseq, argc, sp, passed_block_handler,
(is_lambda ? arg_setup_method : arg_setup_block));
Run Code Online (Sandbox Code Playgroud)
并且在这个功能上。我猜#to_ary是在vm_callee_setup_block_arg_arg0_splat中的某个地方被调用了,很可能是在RARRAY_AREF。我很想阅读这段代码的注释,以了解其中发生的情况。
| 归档时间: |
|
| 查看次数: |
96 次 |
| 最近记录: |