[Day18] 帥氣的特拉法爾加·羅要來告訴大家什麼是 scope!
嗨大家好!今天繼續講在 Ruby 裡要如何執行 Block,
不過在那之前,得先認識兩個概念:scope
和 yield
嗯...以前上英文課不是很認真,這兩個單字怎麼看怎麼陌生,就配著英英字典的解釋一起來看吧:
scope
翻成中文大概就是「作用範圍、作用域」的意思,這個「作用」在程式環境裡指的是能不能生效,很多時候,程式碼只能在一小塊區域內具有效力 (這段文字仍然很抽象)
最常被提到的就是變數,舉個例子,如果像這樣:
i = 9527
def serial_number
puts i
end
serial_number
那麼會印出:
NameError (undefined local variable or method `i' for main:Object)
儘管在第一行已經定義變數 i
的值為 9527,但方法 serial_number
還是找不到要印出來的 i
是什麼,對於程式而言,方法(method)的裡面和外面會被劃分為不同的 scope
,類別(class) 也是同樣的狀況。
yield
翻成中文就是「放棄控制權給其他人」,在程式環境裡,我們會透過 yield
讓方法把控制權暫時轉交給其他程式,什麼意思呢?讓我們繼續看下去~
yield
?雖然昨天好像就有提過,但我這個金魚腦也忘了到底講了什麼,所以還是舉個例子吧:
def we_are_strong
puts "雄壯!"
yield
puts "嚴肅!"
end
we_are_strong {
puts "威武!"
}
# 印出
雄壯!
威武!
嚴肅!
在上面這段程式裡,我在呼叫 we_are_strong
方法時再接一個 Block ,當程式開始執行會依照這樣的路徑:
先執行 puts "雄壯!"
接著執行 puts "威武!"
,因為 we_are_strong
裡寫了一個 yield
,它會讓程式執行到那一行時,把控制權轉出去(離開這個方法)
Block 收到轉出來的控制權,執行自己的動作
Block 執行完後,又會把控制權還給方法
執行 yield
之後的程式
這樣就能讓 Block 在程式裡動起來啦!
yield
還可以攜帶參數像是這樣:
def method_yield
yield 3
end
method_yield { |n|
puts "Hi! " * n
}
# 印出
Hi! Hi! Hi!
不過如果 yield
後面沒寫什麼,就等於是帶了一個 nil
參數。
def method_yield
yield nil
end
method_yield do
p "a"
p "b"
end
# 印出
"a"
"b"
甚至也可以帶多個參數轉出去,而且數量不一樣的話就會發現:
def method_yield
yield 1, 2
end
method_yield do |x, y, z|
p x
p y
p z
end
# 印出
1
2
nil
yield
可以幫助我們對資料進行過濾和篩選,譬如:
list = [* 1..10]
p list.select { |x| x > 5 }
# 印出
[6, 7, 8, 9, 10]
雖然我們看不到 Ruby 的原生方法 select
方法裡面寫什麼,但現在至少已經可以知道,在 select
方法裡一定有一行是 yield
,會把控制權轉交後面接的 Block 計算。
俗話說「禮多人不怪」,既然方法裡的 yield
可以帶參數給 Block,那麼 Block 當然也可以帶東西回去給方法,一個回禮的概念 :D (我說你們感情真好啊!)
譬如:
def test_two(n)
if yield n
puts "yes, it is 2"
else
puts "no, it is not 2"
end
end
test_two
是一個會檢查帶入參數是不是 2 的簡單方法。
如果帶入 3:
test_two(3){|n|
puts "wait...let me check..."
n == 2
}
# 印出
wait...let me check...
no, it is not 2
如果帶入 2:
test_two(2){|n|
puts "wait...let me check..."
n == 2
}
# 印出
wait...let me check...
yes, it is 2
等等!我發現它其實是叫 Block 去做事!(而 Block 也很乖地去做了)
Block 在執行完後會回傳 true
或 false
,然後 test_two
就只是依照這個回傳值來印出相對應的東西,方法內部根本就找不到檢查的機制啊!
block
但是卻用了 yield
就會出錯LocalJumpError
def hello
yield
end
hello
# 印出
LocalJumpError (no block given (yield))
LocalJumpError
的意思是程式不知道你要 yield
到哪裡去
在程式裡,yield
和 block 似乎有一種相對應的共生關係,只是如果在方法裡寫了 yield
,外面卻沒有接收控制權的 Block 的話,Ruby 會噴錯告訴你 LocalJumpError
,意思是程式不知道你要 yield
到哪裡去...
反之,如果只寫了 Block 但方法裡沒有 yield
的話,Block 也只會傻傻地站在方法外面等到天荒地老,直到有一天眾人將它遺忘啊...
所以,請記得讓 yield
和 block 同時出現吧!
介紹就到這邊,希望大家能了解 Ruby 裡的 Block 是什麼了!(也希望之後自己回來看這段不要太精彩)