什么是允许块处理可变数量的参数的简洁方法?

时间:2012-04-16 20:29:01

标签: ruby block proc

我之前遇到过这种情况,有些东西告诉我我一般处理它的方式不是最干净或最惯用的。

假设我有一个带有一个块的函数,它可以反过来带有1个或2个(比方说)参数。

def with_arguments(&block)
  case block.arity
  when 1
    block.call("foo")
  when 2
    block.call("foo", "bar")
  end
end

with_arguments do |x|
  puts "Here's the argument I was given: #{x}"
end

with_arguments do |x, y|
  puts "Here are the arguments I was given: #{x}, #{y}"
end

启用arity似乎非常hacky。有没有更标准的Ruby方法来实现这种事情?

3 个答案:

答案 0 :(得分:5)

以下是我如何将任意参数传递给lambda

def with_arguments(&block)
  args = %w(foo bar)
  n = block.arity
  block.call *(n < 0 ? args : args.take(n))
end

with_arguments &lambda { |foo| }
with_arguments &lambda { |foo, bar| }
with_arguments &lambda { |*args| }
with_arguments &lambda { |foo, *args| }
with_arguments &lambda { |foo, bar, *args| }

如果n为负数,则lambda采用任意数量的参数。准确地(n + 1).abs这些论点是强制性的。可以使用该信息来决定传递哪些参数。

如果lambda获取有限数量的参数,则只需传递n的第一个args元素。如果它需要任意数量的参数,那么只需传递整个参数数组。

lambda本身将处理args不足的情况:

with_arguments &lambda { |foo, bar, baz, *args| }
# ArgumentError: wrong number of arguments (2 for 3)

您可以简单地将两个参数传递给块:

def with_arguments(&block)
  block.call 'foo', 'bar'
end

with_arguments { |x| puts x }              # y is not used
with_arguments { |x, y| puts x, y }        # All arguments are used
with_arguments { |x, y, z| puts x, y, z }  # z will be nil

丢弃未使用的块参数,并将任何额外参数设置为nil

如果给出错误的参数数量,

This is specific to regular blocks and Procs - lambda将引发错误。您可以通过调用Proc#lambda?

来确定是否是这种情况

此外,如果您不打算存储该块,只需使用yield就更清晰了:

def with_arguments
  yield 'foo', 'bar'
end

答案 1 :(得分:0)

一些解决方案......

  • 总是传递2个参数,即使必须为nil
  • 只传递一个数组,该数组可能包含更多或更少的元素。这种方式为您提供了两全其美的优势:由于多个分配的工作方式,您可以省略或提供额外的块参数而无需警告

答案 2 :(得分:0)

def bar(&block)
    puts 'In bar'
    block.call(1) if block
    puts 'Back in bar'
    block.call(1,2) if block
end

1.9.3p392 :043 > bar do |*b| puts b.length end
In bar
1
Back in bar
2