在 程式區塊與 Proc 中談過,Proc物件像是個程式流程片段,有時候,你希望的是程式區塊更像是個方法呼叫,這時可以使用lambda方法。例如:
>> def some(lda)
>> puts "some 1"
>> lda.call
>> puts "some 2"
>> end
=> nil
>> lda = lambda { puts "執行 lambda"; return 1 }
=> #<Proc:0x2161320@(irb):38 (lambda)>
>> some(lda)
some 1
執行 lambda
some 2
=> nil
>>
>> puts "some 1"
>> lda.call
>> puts "some 2"
>> end
=> nil
>> lda = lambda { puts "執行 lambda"; return 1 }
=> #<Proc:0x2161320@(irb):38 (lambda)>
>> some(lda)
some 1
執行 lambda
some 2
=> nil
>>
上例就像是:
def some
def lda
puts "執行 lambda"
return 1
end
puts "some 1"
lda
puts "some 2"
end
def lda
puts "執行 lambda"
return 1
end
puts "some 1"
lda
puts "some 2"
end
然而你仔細觀察,使用lambda方法傳回的物件,其實也是Proc實例:
>> lda = lambda { puts "執行 lambda"; return 1 }
=> #<Proc:0x2161320@(irb):38 (lambda)>
=> #<Proc:0x2161320@(irb):38 (lambda)>
lambda與 直接使用Proc.new建立的物件不同的地方在於,Proc.new直接建立的物件像個程式流程,而lambda方法建立的Proc物件像是個方法,因 此lambda建立時的程式區塊return,就像是個方法return,另外,lambda建立的Proc物件,有哪些區塊參數,call就只能傳入幾 個引數。例如:
>> l = lambda { |a, b| puts a, b }
=> #<Proc:0x27fffa8@(irb):17 (lambda)>
>> l.call(1, 2)
1
2
=> nil
>> l.call(1)
ArgumentError: wrong number of arguments (1 for 2)
from (irb):17:in `block in irb_binding'
from (irb):19:in `call'
from (irb):19
from C:/Winware/Ruby192/bin/irb:12:in `<main>'
>> l.call(1, 2, 3)
ArgumentError: wrong number of arguments (3 for 2)
from (irb):17:in `block in irb_binding'
from (irb):20:in `call'
from (irb):20
from C:/Winware/Ruby192/bin/irb:12:in `<main>'
>>
=> #<Proc:0x27fffa8@(irb):17 (lambda)>
>> l.call(1, 2)
1
2
=> nil
>> l.call(1)
ArgumentError: wrong number of arguments (1 for 2)
from (irb):17:in `block in irb_binding'
from (irb):19:in `call'
from (irb):19
from C:/Winware/Ruby192/bin/irb:12:in `<main>'
>> l.call(1, 2, 3)
ArgumentError: wrong number of arguments (3 for 2)
from (irb):17:in `block in irb_binding'
from (irb):20:in `call'
from (irb):20
from C:/Winware/Ruby192/bin/irb:12:in `<main>'
>>
因此lambda建立的Proc物件,在概念上就類似其它程式語言的一級函式物件,因此在Ruby 1.9之後,直接使用語法支援lambda方法建立的Proc物件。例如:
>> l = ->(a, b) { puts a, b }
=> #<Proc:0x2468880@(irb):21 (lambda)>
>> l.call(10, 20)
10
20
=> nil
>>
=> #<Proc:0x2468880@(irb):21 (lambda)>
>> l.call(10, 20)
10
20
=> nil
>>
圓括號中可以指定參數,若不需要參數,則圓括號可以省略。由於lambda建立的也是Proc物件,所以適用程式區塊的方法,也可以使用lambda建立的Proc物件。例如:
>> [1, 2, 3].each(&->(element) { puts element })
1
2
3
=> [1, 2, 3]
>>
1
2
3
=> [1, 2, 3]
>>
不過這並不是lambda最主要的用途,由於lambda建立的Proc物件更像是個方法,所以你可以將之自由傳遞,也可以從方法中返回,而不用擔心return的問題。一個例子像是 因 式分解,可以先準備好一定長度的質數表,之後利用該質數表來進行因式分解。例如:
# encoding: Big5
class Range
def comprehend(&block)
return self if block.nil?
self.collect(&block).compact
end
end
def prepare_factor(max)
prime = Array.new(max, 1)
2.upto(Math.sqrt(max).to_i - 1) do |i|
if prime[i] == 1
(2 * i).upto(max - 1) do |j|
if j % i == 0
prime[j] = 0
end
end
end
end
primes = (2..max - 1).comprehend { |i| i if prime[i] == 1} # 質數表
->(num) {
list = []
i = 0
while primes[i] ** 2 <= num
if num % primes[i] == 0
list << primes[i]
num /= primes[i]
else
i += 1
end
end
list << num
f = Array.new(list.length, 0)
f.length.times { |i|
f[i] = list[i]
}
return f # 在這邊return是可以不寫的,只是為了強調
}
end
factor = prepare_factor(1000)
p factor.call(100) # 顯示 [2, 2, 5, 5]
p factor.call(500) # 顯示 [2, 2, 5, 5, 5]
p factor.call(800) # 顯示 [2, 2, 2, 2, 2, 5, 5]
當然,如果就這個例子,使用Proc.new直接建立粗體字部份,也可以解決問題:
def prepare_factor(max)
...
Proc.new { |num|
list = []
i = 0
while primes[i] ** 2 <= num
if num % primes[i] == 0
list << primes[i]
num /= primes[i]
else
i += 1
end
end
list << num
f = Array.new(list.length, 0)
f.length.times { |i|
f[i] = list[i]
}
f
}
end
...
Proc.new { |num|
list = []
i = 0
while primes[i] ** 2 <= num
if num % primes[i] == 0
list << primes[i]
num /= primes[i]
else
i += 1
end
end
list << num
f = Array.new(list.length, 0)
f.length.times { |i|
f[i] = list[i]
}
f
}
end
對於一開始就使用Ruby的開發人員而言,程式區塊與Proc.new直接建立物件,確實減少了使用lambda的需求,然 而就熟悉一級函式物件的開發人員而言,對於Proc.new直接建立的物件比較像個程序而不是個方法,以及return的微妙處理方式可能不是那麼熟悉, 使用lambda對他們而言會比較容易掌握,Proc.new直接建立的物件,對他們而言會是另一個選項,多了一份彈性。