首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么从代码块中的双溅参数调用`to_ary`?

为什么从代码块中的双溅参数调用`to_ary`?
EN

Stack Overflow用户
提问于 2019-07-23 11:23:52
回答 1查看 110关注 0票数 3

看起来,双溅块参数在传递的对象上调用to_ary,这与lambda参数和方法参数无关。这一点得到了如下证实。

首先,我准备了一个对象obj,其中定义了一个方法to_ary,它返回的不是数组(即字符串)。

代码语言:javascript
复制
obj = Object.new
def obj.to_ary; "baz" end

然后,我将这个obj传递给具有双溅参数的各种构造:

代码语言:javascript
复制
instance_exec(obj){|**foo|}
# >> TypeError: can't convert Object to Array (Object#to_ary gives String)
代码语言:javascript
复制
->(**foo){}.call(obj)
# >> ArgumentError: wrong number of arguments (given 1, expected 0)
代码语言:javascript
复制
def bar(**foo); end; bar(obj)
# >> ArgumentError: wrong number of arguments (given 1, expected 0)

如上所述,只有代码块尝试通过调用(可能的) obj方法将to_ary转换为数组。

为什么代码块的双溅参数行为与lambda表达式或方法定义的参数不同?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-07-24 21:22:27

我对你的问题没有完整的答案,但我会分享我的发现。

短版

Procs允许使用与签名中定义的参数数目不同的参数进行调用。如果参数列表与定义不匹配,则调用#to_ary进行隐式转换。Lambdas和方法需要匹配它们的签名的args数。不执行转换,这就是不调用#to_ary的原因。

长版

您所描述的是由lambdas (和方法)处理params和procs (和块)之间的区别。看一下这个例子:

代码语言:javascript
复制
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.newKernel.proc创建的procs,额外的参数将被静默丢弃,缺少的参数被设置为nil。(文档)

更重要的是,Proc调整传递的参数以适应签名:

代码语言:javascript
复制
proc{|head, *tail| print head; print tail}.call([1,2,3])
# >> 1[2, 3]=> nil

资料来源:马坎德拉所以问题

#to_ary用于此调整(它是合理的,就像#to_ary用于隐式转换一样):

代码语言:javascript
复制
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=1tail=[2,3]。这和多任务中的行为是一样的:

代码语言:javascript
复制
head, *tail = [1, 2, 3]
# => [1, 2, 3]
tail
# => [2, 3]

正如您已经注意到的,当proc有双溅关键字args时,也会调用#to_ary

代码语言:javascript
复制
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的这两条线中实现的:

代码语言:javascript
复制
    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中。我很乐意阅读这段代码的评论,以了解里面发生了什么。

票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57163086

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档