--- title: Block 的神奇魔法 - & (和號) tags: ASTROCamp, ruby, block --- # Block 的神奇魔法 - & (和號) ## 平常的 `block` 用法 ```ruby def say_hello puts "hello 你好" yield end say_hello { puts "hi 大家好" } # 印出 hello 你好 hi 大家好 ``` ## 使用 `Proc` 物件化 ```ruby @say_hi = Proc.new { puts "hi 大家好" } def say_hello puts "hello 你好" @say_hi.call end say_hello # 印出 hello 你好 hi 大家好 ``` 這邊的 `@say_hi` 我偷偷用了**實體變數**,是因為在 `say_hello` 的 scope 裡面是感應不到**區域變數**的 ## 使用 `lambda` 物件化 ```ruby @say_hi = lambda { puts "hi 大家好" } @say_hi = -> { puts "hi 大家好" } # 兩種寫法都可以 def say_hello puts "hello 你好" @say_hi.call end say_hello # 印出 hello 你好 hi 大家好 ``` ## 那 `Proc` 和 `lambda` 有什麼差別? 1. `lambda` 會檢查代入參數的數目並丟出錯誤 `Proc` 如果**多給參數**,則會無視後面多出來的;如果**少給參數**,則會自動代入 `nil` ```ruby say_hi = -> (x, y) { p "hi #{x}" p "hi #{y}" } say_hi.call("peter") # 出現 ArgumentError say_hi.call("peter", "parker") # 印出 "hi peter" "hi parker" say_hi.call("peter", "parker", "spider") # 出現 ArgumentError ``` ```ruby say_hi = Proc.new { |x, y| p "hi #{x}" p "hi #{y}" } say_hi.call("peter") # 印出 "hi peter" "hi " # nil 印不出來 say_hi.call("peter", "parker") # 印出 "hi peter" "hi parker" say_hi.call("peter", "parker", "spider") # 印出 "hi peter" "hi parker" ``` 2. `lambda` 遇到 `return` 會將控制權交回給呼叫的方法,該方法繼續執行 `Proc` 則是會執行完 `block` 之後直接跳出方法 ```ruby def say_hello say_hi = -> { return "hi 大家好" } say_hi.call "hello 你好" end puts say_hello # 印出 hello 你好 ``` ```ruby def say_hello say_hi = Proc.new { return "hi 大家好" } say_hi.call "hello 你好" end puts say_hello # 印出 hi 大家好 ``` ## 使用 `&` 大絕招 ### 方法一、直接物件化 ```ruby def say_hello(&say_hi) puts "hello 你好" say_hi.call end say_hello { puts "hi 大家好" } # 印出 hello 你好 hi 大家好 ``` 這邊的 `&` 直接將 `say_hello` 後面那一段 `block` 物件化了。 這樣可以在方法裡面直接呼叫,不需使用 `yield` 來轉讓控制權。 很神奇吧! `&` 讓你能夠跳過使用 `Proc` 或 `lambda` 物件化的那一段過程。 ### 方法二、自動轉成 `block` ```ruby even_number = -> (i) { i % 2 == 0 } list = [1, 2, 3, 4, 5] p list.map(&even_number) # 印出 [false, true, false, true, false] ``` 如果寫成 `list.map(even_number)` 會一直出現 `ArgumentError`。 這是因為 `map` 方法本身並不會帶入參數,而這樣寫等於把 `map` 不吃的東西硬塞給它,它就理所當然的~~吐給你看~~噴出錯誤回來。 所以只能在 `map` 後面加上 `block` 讓它進行運算。(方法後面可以接一個 `block`) 但如果在 `even_number` 前面加上 `&`,它就會將這個物件展開成原本的 `block`,再塞給 `map` 吃。 是不是很神奇! 你可以把 `&` 當作是一個縮寫,但對我來說它比較像一個 `toggle`, 會自動判斷何時要將後面的東西物件化,抑或是展開回原本的 `block` 。 ## 動動腦 (參考++大腦喜歡這樣學++) * Ruby 裡面的 `block` 是什麼?有什麼作用?為什麼要使用它? * 如何將 `block` 物件化?為什麼這樣做,有什麼好處? * `Proc` 與 `lambda` 的差別是? * `&` 做了什麼?該如何使用?