Day11 - Ruby的block,proc,lamdba方法比較

前情提要:html

第11天開始,要更深刻Ruby的精髓!react

Ruby經典面試題目#11
Ruby的block,proc,lamdba方法比較?What’s difference between blocks,procs and lambdas?面試

block代碼內存塊數組

代碼內存塊是用do…end圍起來,圍出特定一個區域、放代碼的地方。
就好像跑馬拉松同樣,道路上會進行交通管制,把參賽者的跑道圍起來。ruby

do…end的形式經常使用在數組和循環裏,把數組想成參賽者的列表,循環想成跑道,每一個參賽者(數組內的元素)都要一個一個進入跑道(循環),是否是就很好理解了呢?ide

block: do…end函數

咱們來用do…end圍出block。昨天提到ruby invoke method的階層有五層:ui

find_method = [「Class」,「Module」,「Object」,「Kernel」,「BasicObject」]3d

find_method.each do |find_method|
p find_method
end
結果顯示爲:code

Class
Module
Object
Kernel
BasicObject
block: {}

假如某個特定參賽者選手如我,每一年都一次馬拉松,而2018年即將跑第3次啦!我能夠用大括號{}印出以下:

3.times {p「I love running 42K marathon!」}
由於很重要因此喊3次,這個大括號{}圍出的內存塊,忠實地印出(chao-ok-huangdaoyi):

「I love running 42K marathon!」
「I love running 42K marathon!」
「I love running 42K marathon!」
有沒有注意到,不論是do…end,仍是{},前面都跟着method呢?
do…end前有.each這個Array裏的方法;{}前有.times這個屬於Integer的方法。

因此,重點出現:block不是物件!必須跟在其餘的方法或物件以後。

3.times {p「block is not Object!!!「} #很重要因此說3次
block: yield

人生就像馬拉松同樣漫漫長路。有的時候跑步跑累了,咱們須要喝點水、休息喘口氣。那該如何用block表示呢?咱們使用yield方法呼叫:

def keep_running
p「-start line-」
yield #block
p「-finish line-」
end

keep_running {
p「drink water」
}
結果顯示爲:

-start line-
drink water
-finish line-
在ruby on rails項目裏,咱們也很常在erb檔名下,發現這種利用yield方法,調用代碼內存塊的頁面:

<!DOCTYPE html>
<html>
<head>
<meta http-equiv=「Content-Type」content=「text/html;charset=utf-8」/>
</head>

<body>
<%= yield %>
</body>
</html>
其中<%= yield %>就是在html頁面代入ruby代碼的內存塊。關於更多yield說明,能夠參考Ruby on Rails Guide.

Proc程序物件

Proc是程序物件,跟block同樣可放入程序內存塊。

在Ruby API裏,定義:

Proc objects are blocks of code that have been bound to a set of local variables.
方剛我說明到block不是物件,所以若是咱們遇到須要一次處理不少的block,或是屢次使用一個block的狀況時,與其重複寫code,不如把須要重複的部分寫成物件。

我如今想進一步利用Proc放入程序判斷,計算在馬拉松賽事、半馬和全馬分別跑過幾千米。

首先用block列出參賽紀錄:

place = [「2012太魯閣半馬」,「2012玉山半馬」,「2013萬金石半馬」,「2013雙溪半馬」,「2013臺北市半馬」,「2014黃金海岸半馬」,「2014 Perth半馬」,「2016 Sydney全馬」,「2017 Melbourne全馬」,「2018大堡礁全馬」]

place.each do |place|
puts place
end
block顯示出,凡跑過必留下痕跡!

2012太魯閣半馬
2012玉山半馬
2013萬金石半馬
2013雙溪半馬
2013臺北市半馬
2014黃金海岸半馬
2014 Perth半馬
2016 Sydney全馬
2017 Melbourne全馬
2018大堡礁全馬
以上紀錄顯示,從2012-2018年,半馬(21KM)跑過7次,全馬(42K)跑3次。因爲block沒法代入參數,此時Proc就派上用場了!

Proc的類別方法

來寫我人生第一個使用Proc物件的跑步方法proc_running。
在這個類別方法(class method)內,用.new產生新的程序物件。
在方法以外,用{}大括號圍出block,用.call方法呼叫程序物件proc自己:

def proc_running
Proc.new
end
proc = proc_running {「I love running!」}
p proc.call #=>「I love running」
來看看Proc代入參數以後,能夠作的事就變多囉,我如今想計算半馬(21KM)跑過7次,全馬(42K)跑3次,分別是幾千米:

def count_km(km)
return Proc.new {|n| n*km}
end

full_marathon = count_km(42)#126
half_marathon = count_km(21)#147

p「I've run #{half_marathon.call(7)} Km in Half Marathon and #{full_marathon.call(3)} Km in Marathon」
還記得#{}能夠幫咱們插入字串嗎?(此方法會先利用to_s將傳入的Integer成String格式)
因此計算答案揭曉:

「I've run 147 Km in Half Marathon and 126 Km in Marathon」
Proc的實體方法

Proc能夠new出新的程序物件實體km_proc:

km_proc = Proc.new { |km,*n| n.collect { |n| n*km } }
p km_proc.call(42,1,3)#=> [42,126]
p km_proc.call(21,1,7)#=> [21,147]
這樣我就能夠把計算的跑步千米數,美美地用.collect這個數組方法裱框印出來!

[42,126]
[21,147]
觀察剛剛的類別方法,咱們發現Proc能夠代入參數,也能夠用return回傳參數,那我想要仿造文章前頭這段block代碼概念:

def keep_running
p「-start line-」
yield #block
p「-finish line-」
end

keep_running {
p「drink water」
}
寫一個喝水Proc程序:

def proc_keep_running
p「-start line-」
water_proc = Proc.new { return「drink water」}
water_proc.call
p「-finish line-」
end
p proc_keep_running
# Prints「-start line-」but not「-finish line-」
結果印出:

-start line-
drink water

疑?怎麼喝完水就打住,不繼續跑向終點-finish line-呢?
咱們發現了:

在proc內使用return會當即返回,再也不繼續執行後面程序。
(因此,永遠不能在Proc內使用return,這樣跑馬拉松會沒法抵達終點啊啊啊啊!)

lambda匿名函數

身爲工程師,問題表明着機會;出現問題就表明會有解決方案的出現,在Proc中有一個特別的用法叫lambda。建立方法有兩種指令:lambda or ->()

lambda_running = lambda { puts「Run with lambda!」}
lambda_running = -> { puts「Run with lambda!」}
若是咱們使用puts印出,會發現lambda是Proc的一種:

p lambda_running
#<Proc:0x000056043ddfd1e8@main.rb:11(lambda)>
咱們來用基本的程序語法,比較proc與lambda回傳值:

剛剛說到Proc方法內不能放return,

def proc_run
proc = Proc.new { return }
proc.call
p「Run with Proc!」
end
proc_run #跑不出來「Run with Proc」結果!
但lambda能夠:

def lambda_run
lam = -> { return }
lam.call
p「Run with lambda!」
end

lambda_run #=>「Run with lambda!」
從下文說明,咱們能夠了解Proc只會回傳目前階層的內容,而不會像通常的方法以及lambda匿名方法同樣,整個走完方法裏面的該回傳的值。

The difference between procs and lambdas is how they react to a return statement.A lambda will return normally,like a regular method.But a proc will try to return from the current context.The reason is that you can’t return from the top-level context.出處
如今來用lambda的兩種調用寫法lambda or ->()練習寫做代碼,分別回傳半程馬拉松(hm)和全程馬拉松(fm)的千米數(leafor):

half_proc = lambda {|hm,fm| hm}
full_proc = ->(hm,fm){fm}
p half_proc.call(21,42)#21
p full_proc.call(21,42)#42
結果half_proc.call會回傳21,full_proc.call會回傳42。

若是參數多放一個呢?1.5個馬拉松

p full_proc.call(21,42,63)
會出現錯誤訊息:

main.rb:17:in `block in <main>':wrong number of arguments(given 3,expected 2)(ArgumentError)
這個緣由在於:

lambda是方法,因此它會檢查參數個數是否匹配。
來用程序舉例一下:

lambda_argument = lambda {|x| x*2}
p「Lambda result: 21Km *2= #{lambda_argument.call(21)}Km」

proc_argument = Proc.new {|x,y|「I don't care how many arguments inside Proc!」}
p「Proc result: #{proc_argument.call(21,42,63)}」
結果印出:

「Lambda result: 21Km *2= 42Km」

「Proc result: I don't care how many arguments inside Proc!」
超級比一比的表格又出現了:

block程序內存塊Proc程序內存塊物件lambda匿名方法
不是物件帶名字的內存塊物件,可儲存變數和Proc相似,但更加接近method方法
不是參數可帶參數嚴格檢查參數數目
N/A在Proc裏return其餘值,會離開此物件的方法在lamba裏return其餘值,會回來繼續執行完方法
最後的最後,咱們用lambda來寫馬拉松喝水的方法吧!

def lambda_keep_running
p「-start line-」
water_lambda = lambda { return「drink water」}
p water_lambda.call
p「-finish line-」
end
lambda_keep_running
結果印出:

-start line-
drink water
-finish line-
順利迎向終點finish line了!

也祝你們都能順利完賽!!!:)

相關文章
相關標籤/搜索