---
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` 的差別是?
* `&` 做了什麼?該如何使用?