Ruby:如何检查一个块接受多少参数?

时间:2013-11-07 15:52:28

标签: ruby

我正在研究Ruby,尝试设置一个以块为参数的方法。我知道你这样做是通过给出最后一个参数&前缀,但一旦通过,我该如何验证呢?

如果我想验证参数是否为字符串,我可以使用is_a?(String)。但是,如何验证我收到的是一个接受1个参数的块?还是2?

2 个答案:

答案 0 :(得分:7)

您可以使用Proc#arity方法检查块接受的参数数量:

def foo(&block)
  puts block.arity
end

foo { }        # => 0
foo { |a| }    # => 1
foo { |a, b| } # => 2

来自文档:

  

返回不会被忽略的参数数量。如果   声明块不带参数,返回0.如果块是   已知取n个参数,返回n。如果块有   可选参数,返回-n-1,其中n是强制数   参数。没有参数声明的proc与块相同   声明||作为其论点。

答案 1 :(得分:3)

块不是对象,所以你不能对它们做任何有用的事情(当然除了yield之外)。

我的意思是,没有办法引用它们,它们甚至没有一个名字:

def foo
  yield 'foo'
end

foo do |bar| puts bar end
# foo

foo内部,该块未绑定到任何变量,您甚至无法引用它,因此您显然无法查询其参数。

可以但是要求Ruby将块转换为Proc并将其绑定到参数。然后,您可以按名称引用它,您可以使用完整的Proc API,包括Proc#parameters

def foo(&blk)
  blk.parameters
end

foo do |m1, m2, o1=:o1, o2=:o2, *splat, m3, m4, 
      ok1: :ok1, mk1:, mk2:, ok2: :ok2, **ksplat, &blk| end
# => [[:opt, :m1],
#     [:opt, :m2],
#     [:opt, :o1],
#     [:opt, :o2],
#     [:rest, :splat],
#     [:opt, :m3],
#     [:opt, :m4],
#     [:keyreq, :mk1],
#     [:keyreq, :mk2],
#     [:key, :ok1],
#     [:key, :ok2],
#     [:keyrest, :ksplat],
#     [:block, :blk]]

然而,请注意,“块的arity”的概念在Ruby中是一个毛茸茸的概念,因为块的参数绑定语义松散。块的参数绑定语义不同于方法的参数绑定语义,特别是当涉及到arity时:

  • 如果块只接受一个参数,但传递了多个参数,那不是错误,而是将参数作为绑定到参数的单个Array传递(就好像参数已被声明为*splat参数)。
  • 如果块接受多个参数,但只传递一个参数,则该参数将转换为Array,并且其各个元素将作为参数传递(就好像它们已作为{{1}传递一样参数)。
  • 如果传递的参数多于块接受参数,则忽略额外的参数。
  • 如果传递的参数少于块接受参数的参数,则额外参数将绑定到*splat

总而言之,语义更接近于赋值而不是方法调用。

例如,您会注意到即使nilm1被声明为块中的强制位置参数,m2也会将其类型列为Proc#parameters,即可选参数。

换句话说:即使只声明一个参数的块仍然需要两个参数,并且只能使用一个参数调用声明两个参数的块。

一个有用的示例:整个:opt mixin基于Enumerable 单个元素的方法。但是,对于yield,您确实想要处理两个参数Hash。并且可以,因为key, value Hash#each是两个元素的yield,并且声明两个参数但只接收一个参数的块将“splat”跨参数的参数,以便最终得到绑定到Array的键和绑定到key的值,而不必复制和粘贴所有value方法的双参数版本