不知道各位在剛接觸Ruby的時候 常常對於block感到很困惑,奇怪為什麼可以這樣寫? 疑 為什麼這樣寫會出錯?
今天這篇我就以我自己學習的觀念講解關於block
什麼是block?
5.times { puts "Hello, Ruby" }
#=> 印出 5 次的 Hello Ruby
friends = ["kai", "lin", "ding", "kao"]
friends.each do |friend|
puts friend
#=> 這會把陣列裡的元素一個一個印出來
end
result = 0
array = [2,3,4,5,6]
array.each do |value|
result = result + value #從do到end中間算是一個block。
end
result
#=> 20
#=>實際上執行的內容為:
# 由於array數列中有五個值,因此總共會執行五次,帶入不同的值
result = result + 2
result = result + 3
result = result + 4
result = result + 5
result = result + 6
大括號 { ... } 以及 do ... end,在 Ruby 稱之一個程式碼區塊(Block)
Block 不是物件
我們常會說 Ruby 是一款物件化很徹底的程式語言,在 Ruby 什麼東西都是物件…嗯,但其實還是有少數的例外,例如 Block 本身就不是物件。Block 沒有辦法單獨的存在,也沒辦法把它指定給某個變數,我就是在這邊卡住的,要稍微注意一下。
do 與 {} 的差異
什麼時候該用do,什麼時候該用括弧{}呢?在Stackoverflow上有許多相關討論,可以整理為以下邏輯:
如果邏輯簡單,可以用一行來處理,就盡量使用 {}
相對之下,如果邏輯複雜,則以分行寫的方式處理,使用do...end寫法
result = 0
array = [2,3,4,5,6]
array.each do |value|
result = result + value
end
result
#上面這段程式碼可以改寫成
array.each {|value| result = result + value }
#這樣程式碼一行就搞定而且又清楚
另外,在Rails當中,如果要處理一整串從資料庫挖出來的資料怎麼做?假如我有一堆個人資料要算平均年齡,可以用以下作法:
hash_array = [{:name => "John", :age => 30},
{:name => "Peter", :age => 28},
{:name => "Mary", :age => 32},
]
result = 0
hash_array.each {|person| result += person[:age] }
average = result / hash_array.length
# => 30
Rails中其他常使用的block:collect、map
map和collect兩個Ruby內建的method是專門產生數列用的,可針對每一個數值進行計算。在處理hash或object的時候,可以分別設定為key和value,就可以分開處理:
# 用於數列
array = [3,6,9]
array.map {|value| value * 10}
# => [30, 60, 90]
# 用於物件
hash = {:name => "Kai", :age => 27, :phone => "911"}
hash.map {|key, value| value }
# => ["Kai", 27, "911"]
map和collect用法是相同的,主要是很多其他語言當中同樣的處理方法都叫做collect,因此Ruby也提供了相同的語法給不同習慣的開發者使用。
把 Block 物件化
Block 本身並不是物件,它沒辦法單獨的存在 Ruby 的世界裡,需要依附在方法或物件後面。
但其實也是可以把 Block 物件化,例如使用 Proc 類別:
say = Proc.new { puts "哈囉,Ruby" } # 使用 Proc 類別可把 Block 物件化
要使用它的時候,只要執行這個物件上的 call:
say.call #印出 哈囉 Ruby
如果要帶參數也可以:
say = Proc.new { |name| puts "你好,#{name}"}
say.call("Ruby")
Proc 呼叫方式
要執行一個 Proc 物件,可以使用 call 方法,但其實還有其它好幾種使用方法,例如:
say.call("尼特羅會長") # 使用 call 方法
say.("尼特羅會長") # 使用小括號(注意,有多一個小數點)
say["尼特羅會長"] # 使用中括號
say === "尼特羅會長" # 使用三個等號
say.yield "尼特羅會長" # 使用 yield 方法