4

我正在尝试设置一个将块作为参数的方法。我知道你通过给最后一个参数一个 & 前缀来做到这一点,但是一旦它被传递,我应该如何验证它?

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

4

2 回答 2

11

您可以使用该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 与声明 || 的块相同 作为它的论据。

于 2013-11-07T15:55:02.290 回答
4

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

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

def foo
  yield 'foo'
end

foo do |bar| puts bar end
# foo

在内部foo,块没有绑定到任何变量,你甚至不能引用它,所以你显然也不能查询它的参数。

但是,您可以要求 Ruby 将块转换为 aProc并将其绑定到参数。然后,您可以通过名称引用它,并且您可以使用完整的ProcAPI,包括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]]

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

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

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

例如,您会注意到,即使m1m2在块中被声明为强制位置参数,Proc#parameters它们的类型也会列为:opt,即可选参数。

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

一个有用的例子:整个Enumerablemixin 是基于yield单个元素的方法。但是,对于,您确实要处理两个参数,。你可以,因为s 是两个元素中的一个,并且声明两个参数但只接收一个参数的块将在其参数中“splat”该参数,因此您最终将键绑定到并且值绑定到而没有复制和粘贴所有s 方法的两个参数版本。Hashkey, valueHash#each yieldArraykeyvalueEnumerable

于 2013-11-07T16:24:23.580 回答