程式扎記: [ Ruby Gossip ] Advance : 可執行物件 - 使用 lambda

標籤

2014年12月1日 星期一

[ Ruby Gossip ] Advance : 可執行物件 - 使用 lambda

Source From Here 
Preface 
在 程式區塊與 Proc 中談過,Proc 物件像是個程式流程片段,有時候,你希望的是程式區塊更像是個方法呼叫,這時可以使用 lambda 方法。例如: 
 

上例就像是: 
  1. def some  
  2.     def lda  
  3.         puts "執行 lambda"  
  4.         return 1  
  5.     end  
  6.     puts "some 1"  
  7.     lda  
  8.     puts "some 2"  
  9. end  
使用 lambda 
然而你仔細觀察,使用 lambda 方法傳回的物件,其實也是 Proc 實例: 
>> lda = lambda { puts "test"; return 1 }
=> #<Proc:0x4114c999@(irb):1 (lambda)>

lambda 與直接使用 Proc.new 建立的物件不同的地方在於,Proc.new 直接建立的物件像個程式流程,而 lambda方法建立的 Proc 物件像是個方法,因此 lambda 建立時的程式區塊 return,就像是個方法 return,另外,lambda 建立的 Proc 物件,有哪些區塊參數,call 就只能傳入幾 個引數。例如: 
 

因此 lambda 建立的 Proc 物件,在概念上就類似其它程式語言的一級函式物件,因此在 Ruby 1.9 之後,直接使用語法支援 lambda 方法建立的 Proc 物件。例如: 
>> l = ->(a, b) { puts a, b }
=> #<Proc:0x480d232a@(irb):2 (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]

不過這並不是 lambda 最主要的用途,由於 lambda 建立的 Proc 物件更像是個方法,所以你可以將之自由傳遞,也可以從方法中返回,而不用擔心 return 的問題。一個例子像是 因 式分解,可以先準備好一定長度的質數表,之後利用該質數表來進行因式分解。例如: 
  1. class Range  
  2.     def comprehend(&block)  
  3.         return self if block.nil?  
  4.         self.collect(&block).compact  
  5.     end  
  6. end  
  7.   
  8. def prepare_factor(max)  
  9.     prime = Array.new(max, 1)  
  10.     2.upto(Math.sqrt(max).to_i - 1do |i|  
  11.         if prime[i] == 1  
  12.             (2 * i).upto(max - 1do |j|  
  13.                 if j % i == 0  
  14.                     prime[j] = 0  
  15.                 end  
  16.             end  
  17.         end  
  18.     end  
  19.       
  20.     primes = (2..max - 1).comprehend { |i| i if prime[i] == 1} # 質數表  
  21.   
  22.     ->(num) {  
  23.         list = []  
  24.         i = 0  
  25.         while primes[i] ** 2 <= num  
  26.             if num % primes[i] == 0  
  27.                 list << primes[i]  
  28.                 num /= primes[i]  
  29.             else  
  30.                 i += 1  
  31.             end  
  32.         end  
  33.         list << num  
  34.         f = Array.new(list.length, 0)  
  35.         f.length.times { |i|  
  36.             f[i] = list[i]  
  37.         }  
  38.         return f # 在這邊return是可以不寫的,只是為了強調  
  39.     }  
  40. end  
  41.   
  42. factor = prepare_factor(1000)  
  43. p factor.call(100)  # 顯示 [2255]  
  44. p factor.call(500)  # 顯示 [22555]  
  45. p factor.call(800)  # 顯示 [2222255]  
當然,如果就這個例子,使用 Proc.new 直接建立,也可以解決問題: 
  1. def prepare_factor(max)  
  2.     ...  
  3.     Proc.new { |num|  
  4.         list = []  
  5.         i = 0  
  6.         while primes[i] ** 2 <= num  
  7.             if num % primes[i] == 0  
  8.                 list << primes[i]  
  9.                 num /= primes[i]  
  10.             else  
  11.                 i += 1  
  12.             end  
  13.         end  
  14.         list << num  
  15.         f = Array.new(list.length, 0)  
  16.         f.length.times { |i|  
  17.             f[i] = list[i]  
  18.         }  
  19.         f  
  20.     }  
  21. end  
對於一開始就使用 Ruby 的開發人員而言,程式區塊與 Proc.new 直接建立物件,確實減少了使用 lambda 的需求,然 而就熟悉一級函式物件的開發人員而言,對於Proc.new直接建立的物件比較像個程序而不是個方法,以及 return 的微妙處理方式可能不是那麼熟悉, 使用 lambda 對他們而言會比較容易掌握,Proc.new 直接建立的物件,對他們而言會是另一個選項,多了一份彈性。 

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!