---
# System prepended metadata

title: Ruby Block

---

# Ruby Block
## 特色
1. 一段不會被主動執行的程式碼
2. 無法單獨存活，被物件話後就可以


## 寫法 
do .. end 和 {} 兩種寫法

```ruby=
5.times do |i|
    puts i
end
```

```ruby=
5.times { |i|
    puts i
}
```

## yeild 

### 將控制權轉讓給Block

一般執行程式的狀態 (執行順序 #(順序))

```ruby=
def say_hello #2
    puts "hi, 你好!" #3
end

say_hello #1 #4

puts "there!" #5
```
```
hi, 你好!
there!
```

Block就像寄生蟲一樣，依附在方法後的block不會直接被執行，要看宿主要不要讓你執行
以下加上block的狀況

```ruby=
def say_hello 
    puts "hi, 你好!" 
end

say_hello  {
    puts "here!" #依附在方法後的block
} 

puts "there!"
```
```
hi, 你好!
there!
```

要讓其能被執行要靠 **yeild** 的方法

```ruby=
def say_hello #2
    puts "hi, 你好!" #3
    yield
    puts "hi, 大家好!"#5
end

#1 #6
say_hello {
    puts "here!" #4
} 

puts "there!" #7
```
```
hi, 你好!
here!
hi, 大家好!
there!
```

### 轉移時附加value
控制權轉移的同時，可以附加value送出(可以不只一個)
```ruby=
def say_hello
    yield 3, 5 #這邊的3會被引入到下方的參數n
end

say_hello{ |n ,m|
    puts n #3
    puts m #5
}
```
 當完成之後會自動回傳block最後一行的執行結果，在block裡不使用return
```ruby=

def test_two #2
    if yield 2 #3 #5
        puts "yes" #6
    else
        puts "no"
    end
end

#1
test_two{ |n|
    n == 2 #4 return true
}

#yes
```

應用 : 找到1~100的奇數

```ruby=
p(1..100).select {|n| n.odd?}
```

練習 : 找出陣列中的奇數

```ruby=
def my_select(list)
    result = []
    list.each do |n|
       result << n if yield(n) 
    end
    result
end

p my_select([1,2,3,4,5]) {|i| i.odd?}

p (1..100).select {|n| n.odd?}
```

ruby有倒裝的寫法
```ruby=
if x > 0
  puts "hello"
end
```


可以寫成

```ruby=
puts "hello" if x > 0 
```


的倒裝句寫法，所以：

```ruby=
result << n if yield n
```


其實等於：

```ruby=
if yield n
  result << n
end 
```

## Block 不是參數
如果沒有Block，但卻yield的話，會發生local jump error的錯誤訊息，因為yield不知道要執行哪個Block
```ruby=
def say_hello
    yield
end

say_hello
```

Block不是參數，所以要知道Block存不存在要用 **block_given?** 檢查Block是否存在
```ruby=
def say_hello
    yield if block_given?
end

say_hello #沒有Block不會執行

say_hello{
    puts "hi" #block_given 為 true 所以執行 "hi"
}
```

:::info
不同Block 大括號 vs do ... end 的差異
```ruby=
list = [1,2,3,4,5]

p list.map {|item| 
    # {}結合率比較強，所以再執行的過程中會先執行Block
    item * 2
}


p list.map do |item| 
    # do ... end 結合率比較弱，所以結合率被p 搶走
    item * 2
end
```

{} 的結果 [2, 4, 6, 8, 10]
do .. end 的結果 #<Enumerator: [1, 2, 3, 4, 5]:map>
:::

### 物件化 Block

將 block 物件化後，透過給予引數來執行，物件化的方式有下面幾種

```ruby=
add_two = Proc.new {|n| n+2} #方法一
p add_two.call(3) #比較常用call
p add_two[3]
p add_two.(3)
p add_two.===(3)

add_two2 = lambda {|a| a + 2} #方法二  (lambda)
p add_two2.call(3)

add_two3 = -> (x) { x + 2 } #方法三 (lambda)
p add_two3.call(3)
```

Proc vs Lambda

Proc 多給引數不會發生錯誤，Lambda會發生錯誤
```ruby=
add_two = Proc.new {|n| n+2}
p add_two.call(1,2,3) # 印出3

add_two2 = lambda {|a| a+2}
p add_two2.call(1,2,3) #跳error
```