2012-04-16 46 views
3

我以前遇到過這種情況,有些事情告訴我,我通常處理它的方式並不是最乾淨或最習慣的方式。什麼是乾淨的方式來允許塊來處理可變數量的參數?

假設我有一個功能,需要一個塊,它可以反過來採取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方法來實現這種事情?

+0

不你將這個方法分解爲'with_one_argument'和'with_two_arguments'(當然它們不會被稱爲那個)? – 2012-04-16 20:34:03

回答

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需要有限數量的參數,那麼只需傳遞args的第一個n元素。如果它需要任意數量的參數,那麼只需傳遞整個參數數組。

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如果給定的參數數量錯誤,s會引發錯誤。實際上,你可以找出是否是這種情況通過調用Proc#lambda?

另外,如果你不打算來存儲塊,它是清潔,以簡單地使用yield

def with_arguments 
    yield 'foo', 'bar' 
end 
+1

也使用產量更快。 – megas 2012-04-16 21:01:19

+1

啊,我再一次被咬了,因爲沒有包含完全符合我的用例的示例代碼。這當然是一個有用的答案;但事實證明,我*主要關心服用'lambda'。對此限制的任何想法? – 2012-04-16 21:24:43

+0

@丹濤,更新了答案。請看一下。 – 2012-04-16 21:40:18

0

一些解決方案...

  • 總是傳遞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 
相關問題