0%

Ruby on Rails 學習筆記-Block

Block的寫法有哪些?

  • do..end
1
2
3
5.times do |i|
puts i
end
  • 花括號{ }
1
2
3
5.times { |i|
puts i
}

Note: block裡面可帶參數進去,例子中是|i|,但這個i只在block裡面作用,類似JS匿名function的參數。

兩種寫法有何不同?

一般我們遇到比較可以一行搞定的,通常就會選擇花括號,而比較負責的邏輯判斷時,則使用do..end寫法,但這兩種方法還是有一些差異存在,以下舉一個簡單的例子給大家看看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
array = [1, 3, 5, 7]

p array.map{ |a| a * a }

p array.map do
|a| a * a
end

ruby常省略小括號,原型其實是:
p(array.map{ |a| a * a }) # 印出 [1, 9, 25, 49]

p(array.map) do #印出 <Enumerator: [1, 3, 5, 7]:map>
|a| a * a
end

上面兩種結果顯示do..end這個寫法的結合力較差,在這個例子中的array.map優先跟p結合,而不是後面的block,所以後面的block無法被順利執行。

Block不是物件,無法獨立使用

1
2
3
{ puts "hello" } #出現語法錯誤

do puts "hello" end #語法錯誤

使用Proc、lambda將block物件化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#使用Proc
times_five = Proc.new { |x| x * 5 } #後面的block變成一個物件,並且可以傳參數進去

p times_five.call(3) #印出15
p times_five. === 3 #印出15
p times_five.(3) #印出15
p times_five[3] #印出15
p times_five.yield(3) #印出15

#使用lambda
times_five = lambda {|x| x * 5} #後面不需要加new方法
times_five = -> (x) { x * 5} #lambda的另一種寫法

p times_five.call(3) #印出15
p times_five. === 3 #印出15
p times_five.(3) #印出15
p times_five[3] #印出15
p times_five.yield(3) #印出15

#兩種方式使用上的不同
times_five = Proc.new { |x| x * 5 } #執行像是block
p times_five.call(1, 3, 5) #正常執行,但只帶第一個引數,印出5。如果絕對值裡面有兩個參數,就可以帶兩個引數進去

times_five = lambda {|x| x * 5} #執行像是方法,參數和引數數量必須相同
p times_five.call(1, 3, 5) #wrong number of arguments (given 3, expected 1) (引數個數錯誤)

Block是如何被執行的?

可使用yield將控制權交給block執行內容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def greeting
puts "hello, phil"
yield
puts "let's go lakers"
end

greeting{
puts "have you watched NBA final today?"
}

puts "2 more wins to go!"

#印出
hello, phil
have you watched NBA final today?
let's go lakers
2 more wins to go!

應用block概念土炮手動.select

1
2
3
4
5
6
7
8
9
10
11
def my_select(list)
result = [] #製作空陣列
list.each do |n| #將list裡面的每個數字拋進來
result << n if yield(n) #將數字再給回下面block執行,如果成立,丟到result的陣列裡面
end
result #回傳結果
end

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

#印出[1, 3, 5]