ruby - 为什么从代码块中的双拼参数调用 `to_ary`?

标签 ruby lambda parameters codeblocks

似乎双拼 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 using Proc.new or Kernel.proc, extra parameters are silently discarded and missing parameters are set to nil. (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=1tail=[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_aryvm_callee_setup_block_arg_arg0_splat 的某处被调用,很可能在 RARRAY_AREF 中。我很想阅读这段代码的评论,以了解其中发生的事情。

关于ruby - 为什么从代码块中的双拼参数调用 `to_ary`?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57163086/

相关文章:

ruby - Emacs shell 找不到 ruby​​ gems——例如,找不到 RubyGem haml (>= 0) (Gem::LoadError)

c++ - 使用引用作为通用参数从通用 lambda 创建线程

java - 是否有可用于任何 lambda 的无操作 (NOP) 的方法引用?

f# - F#中的传递函数

function - 在 Scala 的 Actor 中传递函数并对其结果进行操作

具有多个带空格参数的 Java ProcessBuilder

c++ - "not in this scope"cpp函数调用错误

ruby - 如何将 Ruby 的 STDIN 传递给 Open3.popen3 调用的外部程序?

ruby-on-rails - Ruby On Rails - 当 gem 在其 Gemfile HTTP 源中使用时,中间人攻击?

ruby - 启动 Knife 配置命令时出错