哇嗚~竟然來到了鐵人賽2/3的里程盃了。可喜可賀,恭喜老爺,賀喜夫人!
Day20 說明Ruby裡Enumerable的實用之處? Why Is Enumerable So Useful?
列舉(Enumerables)實用之處在於,能夠幫我們方便地走過、搜尋、排序或轉換集合
內的元素(traverse, sort, search, and transform collections)。
而集合(Collection)跟斯斯一樣有三種:陣列Array
,雜湊Hash
,範圍Range
,在前19天練習經典題目時,我都剛好有碰到、並透過舉例了解如何使用集合
。
今天就以陣列為例,來進一步深入了解列舉
的各項技能!
第14天時我們曾經比較過each
和 map / collect
。延續昨天的舉例,我們來使用季節作為陣列元素:
season = ["Spring",
"Summer",
"Autumn",
"Winter"]
p season.each
Output:
#<Enumerator: ["Spring", "Summer", "Autumn", "Winter"]:each>
看到了Enumerator
這個字眼!
.sort
方法會幫我們按造字母順序重新排序:
p season.sort #["Autumn", "Spring", "Summer", "Winter"]
p season #["Spring", "Summer", "Autumn", "Winter"]
加了!
驚嘆號的method通常是提醒我們要小心!注意!因為此方法會改寫原本物件裡的值。在這裡的陣列已經被新的排序改寫:
p season.sort! #["Autumn", "Spring", "Summer", "Winter"]
p season #["Autumn", "Spring", "Summer", "Winter"]
(.sort!
會造成氣候變遷~季節大亂啊...)
Ruby提供許多語法幫我們完成搜尋任務。現在就來一一介紹吧!
我們可以用find_index
搜尋某個元素的index(索引值,從0開始):
p season.find_index("Summer") # 印出index=1
搜尋陣列中的第1個元素,可以用.first
方法:
p season.first #"Spring"
Question: 我們想要搜尋出季節陣列裡,開頭是
S
的元素。此題怎解?
在[第9天]時提過字串有許多的方法,這邊要介紹新的兩種: .include?
與match()
String#include?
+ 字元符號
Ans: 陣列方法.select
可以幫忙做挑選;而在Block{}
裡面,用String#include?
做條件判斷,像個過濾網一樣,找出包含大寫'S'
開頭的元素:
p season.select {|s| s.include? 'S'}
# ["Spring", "Summer"]
p season.select {|s| s.include? 't'}
# ["Autumn", "Winter"]
String#match()
代入正規表達式正規表達式(Regular Expression,常簡寫為RegExp
、regex
或RE
)是被用來匹配字串String中,字元組合的模式。
想像我們在用Google大神搜尋資料,最重要的就是輸入關鍵字
了。有時候我們會在關鍵字加入+
加號、|
(OR),或是用-
減號剔除篩選字。正規表達具有類似的功能。
同樣的,如果我們想要在進階搜尋的話,就必須使用到正規表達式。
Q2. 我們想要搜尋出季節陣列裡,第二個字母是
u
的元素。此題怎解?
p season.select {|s| s.match(/^*u/)}
# ["Summer", "Autumn"]
/正規表達符號/
放在一組斜線/
裡面,以上的例子,我們需要將^
開頭的第一個字元設定為任意字元*
,第二個字元為u
(正規表達規則請見Ruby API說明)。
我們也可以省略String Method,直接在在block
內用元素s[/加入正規表達/
]來篩選符合條件的元素
p season.select{|s| s[/^*u/]}
# ["Summer", "Autumn"]
同樣解法,且程式碼更少的簡便寫法:
p season.grep(/^sa/)
# []
更多的.grep
用法看這裡
在實務上,我們常常將集合內的值做轉換,套用到需要的場景,第14天時學過的 .map
可以在此時派上用場。
北半球的天氣漸漸變冷,我來寫一個溫度陣列temperature
,並用.map
放入新的零下溫度陣列,但不影響到原本陣列裡的值 :
temperature = [0,1,2,3,4,5,6,7,8,9]
minus_temp = temperature.map{|i| -i}
p temperature.join(", ") #=> "0, 1, 2, 3, 4, 5, 6, 7, 8, 9"
p minus_temp.join(", ") #=> "0, -1, -2, -3, -4, -5, -6, -7, -8, -9"
加了!
驚嘆號的方法又出現了~大家應該可以猜到.map!
會改變陣列值:
temperature = [0,1,2,3,4,5,6,7,8,9]
temperature.map!{|x| x -x} #將"0, -1, -2, -3, -4, -5, -6, -7, -8, -9"與原陣列各個元素相加
p temperature.join(',')
#=> => "0,0,0,0,0,0,0,0,0,0"
全部變0度啦!
enumerable
結合yield
[昨天]講過yield
的功用多多,今天來把它和列舉變數
結合,再產生一次新火花~:)
class Winter #北半球冬天
include Enumerable #包含Enumerable Module(否則會出現 NomethodError)
def initialize
@month = %w[ December January Fabruary]
end
def each # 定義each方法(否則Winter.new.map 會出現 NoMethodError)
for month in @month do
yield month
end
end
end
p Winter.new.map { |mth| mth.upcase }
# 將月份改成大寫
Output:
["DECEMBER", "JANUARY", "FABRUARY"]
天氣轉涼了,大家要注意保暖唷:)
Ref:
謝謝這一系列的文章
學到很多
以下是我剛才測試的結果
在 Regexp
* 是0個以上的字元 => /^*u/ 代表u放在字串的任何地方都符合
. 是所有字元 => /^.u/ 代表u只能放在字串的第二個位置
'aasdfu'.match(/^*u/)
=> #<MatchData "u">
'uasdf'.match(/^*u/)
=> #<MatchData "u">
'aasdfu'.match(/^.u/)
=> nil
'auasdfu'.match(/^.u/)
=> #<MatchData "au">