在前面章節中,不曉得你有沒有看到像是這樣:
arr = [1, 2, 3, 4, 5, 6, 7]
p arr.reduce { |sum, x| sum + x }
1.upto(5) do |i|
puts "hi, ruby #{i}"
end
{...}
, do
end
這個就是一個區塊(block)。
Block 在 Ruby 或 Rails 裡會大量的看到他,所以這是非常常見的語法。
那 Block 是什麼呢?
他就是一段程式碼,一段不會被主動執行的程式碼,接下來就那你們進入 Block 的世界吧!
其實 Block 分成 {...}
跟 do ... end
,而且寫法是可以互換的:
# do ... end
5.times do
puts "Hello World!"
end
# 大括號
5.times {
puts "Hello World"
}
但通常遇到就複雜的判斷會使用 do ... end
,但一行就能寫完的東西就會用{ ... }
。
雖然 Ruby 是物件化的程式語言,但還是有例外,例如 Block 本身就不是物件。
記住 Block 是沒辦法單獨的存在,所以你執行下面兩行程式,就會出現語法錯誤。
{ puts "Hello, World!" } # 語法錯誤
action = { puts "Hello, World!" } # 語法錯誤
既然這樣會出錯,那要怎樣才能執行呢?
首先你必須給他欉控制權他才會執行,如下:
def go_where
puts "go to aquarium!"
end
go_where {
puts "today"
}
puts "go!go!go!"
這樣就可以成功印出:
go to aquarium!
go!go!go!
這時候,你就問為甚麼 today 沒有印出來!!
就讓我娓娓道來!
在馬路上,大家應該很常看到這個標誌吧!
看到他的時候,就要禮讓對方。
那在程式中寫下 yield
之後,他會把控制權暫時轉讓給 Block 。
讓我們來看看他怎麼把控制權轉讓出去吧!
def go
puts "go to aquarium!"
yield
puts "go to zoo"
end
go {
puts "today"
}
puts "tomorrow"
這樣就會印出:
go to aquarium!
today
go to zoo!
tomorrow
在轉讓的同時,yield
會順便帶上一些伴手禮給 Block:
def one_num
yield 5
end
one_num do |n|
puts n # 印出 5
end
def two_nums
yield 12, 25
end
two_nums do |x, y|
puts "#{x}月" # 印出 12月
puts "#{y}日" # 印出 25日
end
其實 Block 也會帶一些東西回來,那他要怎麼帶東西回來呢?
def test_two
if yield(5)
puts "yes, it is 2"
else
puts "no, it is not 2"
end
end
test_two {|n| n == 2 }
因為在 Block 的最後一行就會是他的回傳值,所以上面的程式碼會印出:no, it is not 2
另外,Block 也不會使用 Return :
p (1..10)select{ |m| m.even? }
p (1..10)map{ |n| n**2 }
如果你不小心用 return 回傳 Block 的結果:
p (1..10)select{ |m| return m.even? }
那就會出現 LocalJumpError
錯誤
因為 Block 不是一個方法,它不知道你要 Return 到哪而造成錯誤。
最後我來說說這兩個的差別吧!
一開始有說這兩個是可以互換的,但是他們還是有一些小小的差別。
他們的差別在於優先權,如下:
p (1..5).map{ |n| n**2 } # 印出 [1, 4, 9, 16, 25]
p (1..5).map do |n| n**2 end # 得到 <Enumerator: 1..10:map>
會造成不同結果的原因,有點像是數學的「先乘除後加減」,{}
的優先順序較高,其次是do end
。
讓我們來還原一下:
p ((1..5).map{ |n| n**2 })
p ((1..5).map) do |n| n**2 end # 優先順序較低,所以變成先跟 p 結合,造成後面的 Block 就被忽略了。
以上就是有關block的介紹。
參考資料: