淺談Ruby中的block, proc, lambda, method object的區別

前言

當你們在百度中搜索「block proc lambda」的時候,會出來不少關於這幾個概念之間區別的介紹,既然搜索結果中已經有了這些介紹,那爲何還要寫這篇文章?react

相信看過百度搜索結果中排名靠前的幾篇文章的同窗,都會發現其實這些文章並無很好的說明他們之間區別是什麼,大多隻是介紹各自的用法,加上些許的區別,即便個別介紹了一些區別,也不夠系統,不夠深刻。算法

正是基於上述緣由,才醞釀了本文。本文以簡單示例的方式,詳細的介紹了它們之間的區別。相信您閱讀完本文,必定會豁然開朗,並在從此的開發中準確並優雅的使用它們。編程

因爲時間,我的能力水平等有限,本文不免有錯誤或缺失之處,歡迎不吝指出。ruby

block & proc

在介紹它們的區別以前,咱們先來看一段有關block的簡單使用方法:閉包

   def use_yield                                                                                         
       yield
   end
   
   use_yield do
       puts 'use yield'
   end
   
   def use_block_call(&block)
       block.call
  end
 
  use_block do
      puts 'use block call'
  end

 

以上介紹了兩種在函數中使用block的方式,第一種是經過yield來使用block,另一種則是經過block.call來使用block。函數

proc全稱爲procedure,中文翻譯爲過程,步驟。關於block和proc的區別,咱們先來看一個簡單的示例。spa

   def what_am_i(&block)                                                                                 
       block.class
   end
   puts what_am_i {}  # =< Proc

 

定義一個函數what_am_i並接受一個block,執行體中打印了block的類型,從執行的結果咱們看到block的類型爲proc,即說明block爲proc的一種。翻譯

block和proc之間的區別主要有兩個:一是proc能夠重複使用,若是某個block將在多個地方被用到,則咱們能夠將其定義爲proc。另一個區別就是當某個函數須要執行多個閉包的時候,此時咱們就沒法使用block而只有使用proc或其餘的閉包。code

示例以下,在執行某個具體的操做的先後,調用了咱們本身的proc。ip

   def action(code)                                                                                      
       code[:before].call
       puts 'processing...'
       code[:after].call
   end
  
   action :before => Proc.new {puts 'before action'},
       :after => Proc.new {puts 'after action'}

 

proc & lambda

關於proc和lambda的區別,咱們先來看一個簡單的例子。

   def args(code)                                                                                        
       code.call 'one','two'
   end
  
   args Proc.new { |a,b| puts a,b}
   args lambda {|a,b| puts a,b}

 

上述示例,第一眼看去發覺lambda和proc之間沒什麼區別或者很難發現它們的區別是什麼。

接下來,咱們對上述示例作一下簡單的修改,咱們將以前的接受兩個參數修改成三個,看看會發生什麼狀況。

   def args(code)
       code.call 'one','two'
   end
   
   args Proc.new { |a,b,c| puts a,b,c}
   args lambda {|a,b,c| puts a,b,c} # =< wrong number of arguments (2 for 3) (ArgumentError)

 

運行修改後的示例,發現lambda閉包出錯了,出錯的信息爲,錯誤的參數個數。

至此,咱們很快就發現了它們之間的一個區別是,lambda會檢查參數的個數,而proc則不會,proc默認將缺失的參數視爲nil,並繼續執行代碼,這是爲何呢?在本節的最後,咱們會道出這其中的原因。

在瞭解到它們的第一個區別以後,接下來咱們再來看一個示例。

   def proc_return•                                                                                      
       Proc.new {return 'Proc.new'}.call
       puts 'proc_return method finished'
   end
   
   def lambda_return
       lambda { return 'lambda'}.call
       puts 'lambda_return method finished'
   end
   
  puts proc_return # =< Proc.new
  puts lambda_return # =< lambda_return method finished

 

這個示例很是的簡單,咱們定義了兩個函數proc_return以及lambda_return。這兩個函數一個用proc實現,另一個用lambda實現,執行體都只有一行代碼,就是返回一段字符串。

執行的結果出乎咱們的意料,proc並未執行return以後的代碼,而lambda執行了return以後的代碼。

綜上所述,咱們得出了proc和lambda的兩個重要區別,一是lambda會進行參數個數的檢查而proc則不會,另外lambda會執行return以後的代碼而proc則不會。

爲何會出現上述狀況,本質的緣由在於,proc只是一段嵌入的代碼片斷而lambda則是匿名函數,正由於是匿名函數,因此會檢查函數調用參數,並在函數調用結束以後,繼續執行後面的代碼,而proc因爲是嵌入的一段代碼片斷,在執行完return語句後,就已經返回,因此再也不執行以後的代碼。

lambda & method object

   def greeting                                                                                          
       puts 'hello, method object'
   end
   
   method(:greeting).call
   
   lambda{puts 'hello, lambda'}.call

 

lambda和method object的用法基本一致,其惟一的區別在於lambda爲匿名函數,而method object爲命名函數。

總結

關於block,proc,lambda, method object這四者之間的區別可總結爲如下:

block和proc本質上是一段嵌入的代碼塊,並不是函數。而lambda和method object都是函數,只不過lambda是匿名函數,而method object爲命名函數。

從本質上理解了它們的區別,咱們在從此的開發中就會正確且優雅的運用它們。

引用

[1] http://www.reactive.io/tips/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/

相關代碼

若是您對算法或編程感興趣,歡迎掃描下方二維碼並關注公衆號「算法與編程之美」,和您一塊兒探索算法和編程的神祕之處,給您不同的解題分析思路。

相關文章
相關標籤/搜索