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

j8yoct9x  于 2023-08-04  发布在  Ruby
关注(0)|答案(2)|浏览(111)

我正在尝试建立一个方法,它以一个块作为参数。我知道你通过给最后一个参数一个&前缀来做到这一点,但是一旦它被传递了,我应该如何验证它呢?
如果我想验证一个参数是字符串,我可以使用is_a?(String)。但是我如何验证我已经收到了一个接受一个参数的块?还是两个?

jpfvwuh4

jpfvwuh41#

你可以使用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与声明||作为其论据。

xnifntxz

xnifntxz2#

块不是对象,所以不能对它们做任何有用的事情(当然,除了对它们使用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,其单个元素将作为参数传递(就像它们已作为*splat参数传递)。
  • 如果传递的参数多于块接受的参数,则忽略多余的参数。
  • 如果传入的参数少于块接受的参数,则额外的参数将绑定到nil

总而言之,与方法调用相比,其语义更接近于赋值。
例如,您会注意到,即使m1m2在块中被声明为强制位置参数,Proc#parameters也将其类型列为:opt,即可选参数。
换句话说:即使一个块只声明一个参数,仍然需要两个参数,而一个块声明两个参数,可以调用只有一个参数。
这是一个有用的例子:整个Enumerable mixin是基于yield一个 * 单一 * 元素的方法。但是,对于Hash,您实际上需要处理两个参数key, value。你可以,因为Hash#eachyield是一个Array,有两个元素,一个声明了两个参数但只接收一个参数的块会在它的参数之间“splat”这个参数,这样你就可以得到绑定到key的键和绑定到value的值,而不必复制和粘贴两个-所有Enumerable方法的参数版本。

相关问题