在剛開始接觸Ruby或Rails時,一定對於block感到很困惑(至少筆者是這樣認為啦...),假如又傻傻用scaffold架起一個Rails專案,更是看不懂裡面在幹嘛。以下是常見的Ruby block用法:
result = 0
array = [2,3,4,5,6]
array.each do |value|
result = result + value
end
result
# => 20
由此可知,上頭的each block實際上執行的內容為:
# 由於array數列中有五個值,因此總共會執行五次,帶入不同的值
result = result + 2
result = result + 3
result = result + 4
result = result + 5
result = result + 6
前一個章節的do...end例子,也可簡寫為以下:
array.each {|value| result = result + value }
在作用一樣的情況下,由於邏輯較簡單,這樣的寫法較容易整理。
不過什麼時候該用do,什麼時候該用括弧{}呢?在Stackoverflow上有許多相關討論,可以整理為以下邏輯:
例如以下的用法就不適合寫在同一行:
array.each do |value|
result = result + value
another_array << result if i > 10
# 牽扯的邏輯比較複雜,如果寫在同一行會很吃力且不必要
end
另外,在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
讓整個加總在map裡面完成,簡單易懂。
map和collect兩個Ruby內建的method是專門產生數列用的,可針對每一個數值進行計算。在處理hash或object的時候,可以分別設定為key和value,就可以分開處理:
# 用於數列
array = [3,6,9]
array.map {|value| value * 20}
# => [60, 120, 180]
# 用於物件
hash = {:name => "John", :age => 30, :phone => "0910111000"}
hash.map {|key, value| value }
# => ["John", 30, "0910111000"]
補充:map和collect用法完全相同,主要是很多其他語言當中同樣的處理方法都叫做collect,因此Ruby也提供了相同的語法給不同習慣的開發者使用。
在Rails當中,假如我們要從資料庫挖出所有使用者的姓名,利用each可以寫法如下:
names = User.all.map {|user| user[:name] }
# 組成一個全部都是名字的數列
但如果只針對數列中的每一個值使用一個method,那可以再更簡化為:
names = User.all.map(&:name)
那個很奇異的&:符號代表著一個block的縮寫,而name就是帶入各個User要使用的method。
詳細的討論可以在Stackoverflow上找到。
Block vs Brackets
Map & Collect
&: to_proc
本文同步刊登於我的部落格:特快車