組み込みクラス: modules編

RExをやっている限り頻出範囲という印象を受けるので、ちゃんと覚えておきたい。

Enumeratable

Enumeratableモジュールはeachメソッドが定義されたクラスで使用可能となる。

map, collect破壊的

与えられたブロックを評価した結果の配列を返す。 ブロックを与えなくても、エラーにはならず、Enumeratorオブジェクトが返る。

irb(main):038:0> array = [1,2,3,4,5,6,7,8,9]
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
irb(main):039:0> array.map
=> #<Enumerator: [1, 2, 3, 4, 5, 6, 7, 8, 9]:map>
irb(main):040:0> array.map{|a| a * 2}
=> [2, 4, 6, 8, 10, 12, 14, 16, 18]
irb(main):042:0> p array
[1, 2, 3, 4, 5, 6, 7, 8, 9]
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]

each_with_index

ブロックに、配列の中身とインデックスを渡す。 mapやcollectと異なり、あくまでeach~endの延長線なので、評価済みの配列が返ってくるわけではない。返り値は元の配列にすぎない。

ただし、インデックスは渡さなくてもエラーにはならないようで、配列の中身だけがブロックに渡される。

irb(main):044:0> array.each_with_index{|v| p v}
1
2
3
4
5
6
7
8
9
irb(main):048:0> array.each_with_index{|v,i| p v * (2*i)}
0
4
12
24
40
60
84
112
144
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]

inject

なんか不自然な気もしてしまうが、エイリアスreduceがある。 畳み込み演算、というものを行うメソッド。

YOUたち!RubyでinjectしちゃいなYO!

injectの引数を初期値として、初期値と配列の先頭を使った演算を行い、次にその結果と配列の2番目を使った演算を行い....ということを繰り返す。

injectの引数は省略でき、その場合は初期値は配列の先頭、最初の演算は「配列の先頭と2番目」を使って行われる。

irb(main):061:0> [1,2,3,4,5,6,7].inject(1){|init,new_val| init + new_val }
=> 29
irb(main):067:0> [1,2,3,4,5,6,7].inject(0){|init,new_val| init + new_val }
=> 28
irb(main):068:0> [100,1,2,3,4,5,6,7].inject{|init,new_val| init + new_val }
=> 128

each_cons

引数で渡された数ずつ、要素を配列で渡していく。一回に渡す要素は渡す回数が増えることに、indexがインクリメントされていく。 例えば10個の要素に対してこのメソッドに3の引数が渡されたら、要素は「1,2,3」「2,3,4」「3,4,5」...となる。これが繰り返される。

irb(main):071:0> [1,2,3,4,5,6,7].each_cons(2){|items| p items.class}
Array
Array
Array
Array
Array
Array
=> nil
irb(main):074:0> [1,2,3,4,5,6,7].each_cons(2){|items| p items}
[1, 2]
[2, 3]
[3, 4]
[4, 5]
[5, 6]
[6, 7]
=> nil
irb(main):073:0> {:a => "tokyo", :b => "maebashi", :c => "yokohama", :d => "odawara", :e => "ehime"}.each_cons(2){|items| p 
items }
[[:a, "tokyo"], [:b, "maebashi"]]
[[:b, "maebashi"], [:c, "yokohama"]]
[[:c, "yokohama"], [:d, "odawara"]]
[[:d, "odawara"], [:e, "ehime"]]
=> nil

相手が配列だろうが、ハッシュだろうが、要素は配列で渡されている。

each_slice

要素を指定された数で「区切って」渡す。(each_consとの違いは「区切り=slice」と覚える。)

当然、配列の最後部では指定された要素に足らないことも起こりえる。

irb(main):002:0> [1,2,3,4,5,6,7,8,9].each_slice(4){|items| p items}                                                         
[1, 2, 3, 4]     
[5, 6, 7, 8]     
[9]
=> nil

reverse_each

eachを逆順に要素を渡す。

all?, any?, one?, none?

trueを返す条件はそれぞれ

all?: 配列のすべて any?: 真が一つでもあれば one?: 真が一つだけあれば none?: 真が一つもなければ

メソッド 意味
all? 配列のすべて
any? 真が一つでもあれば
one? 真が一つだけあれば
none? 真が一つもなければ

後ろにブロックをとって判定することもできる。

irb(main):007:0> [false,false,"HelloWorld",false,false].one?
=> true
irb(main):011:0> [0,0,0,false,0].none?
=> false

C言語では0はfalseと同意だが、Rubyでは,nilfalse以外はtrueになるので、注意する。

irb(main):013:0> [false,false,true,false,false].one?{|item| p item}
false
false
true
false
false
=> true
irb(main):014:0> [false,false,true,false,false].any?{|item| p item}
false
false
true
=> true

oneは配列のすべての要素を確認して、配列に一つだけしか存在しないことを確かめる必要があるのに対して、anyは配列に一つでも要素があればtrueを返せる。よって処理の長さも各メソッドで異なる。

find

エイリアスdetect。ブロック評価で最初に真になる要素を返す。

(selectとのエイリアスの混同が起こりそうだが、deletectは「検知」を意味するので「検知をした時点で処理を終了している」と考えてみる。)

find_all

エイリアスselect (「selectボタンは複数選択」と覚えてみる。)

reject

find_allと対の関係。ブロック評価でfalseになったものすべて。

grep

===メソッドでイコールの関係になるものすべてが返ってくる。正規表現オブジェクトを引数に持ってくることを想定していると思われる。

irb(main):017:0> [1,3,5,3].grep(3)
=> [3, 3]

sort_by

sortと似たような動きをするが、ブロックを渡すと、それを評価基準としてソートを行う。sortでは、ブロックを渡す場合2つ以上の要素が渡されないと意味がないが、sort_byの場合、これは一つで良い。

irb(main):019:0> [100,10,10000,99].sort_by{|item| item}
=> [10, 99, 100, 10000]
irb(main):023:0> [100,10,10000,99].sort{|item| item}
=> [99, 10000, 10, 100]

max_by,min_by

sortとsort_byの違いのような関係。max_byを用いるとブロックに渡すのは「評価基準」だけで良い。一方sortでブロックを渡した場合、ソート処理の方法に関しても記述する必要がある。

irb(main):028:0> ["100","10","10000","99"].max{|item| item.length}
=> "99"
irb(main):030:0> ["100","10","10000","99"].max{|i,i2| i.length <=> i2.length}
=> "10000"
irb(main):031:0> ["100","10","10000","99"].max_by{|i| i.length}
=> "10000"

group_by

ブロックの評価結果をキーとして、同じキーを持つ要素を配列にしたハッシュを返す。

irb(main):032:0> (1..100).group_by{|v| v % 7}
=> {1=>[1, 8, 15, 22, 29, 36, 43, 50, 57, 64, 71, 78, 85, 92, 99], 2=>[2, 9, 16, 23, 30, 37, 44, 51, 58, 65, 72, 79, 86, 93, 100], 3=>[3, 10, 17, 24, 31, 38, 45, 52, 59, 66, 73, 80, 87, 94], 4=>[4, 11, 18, 25, 32, 39, 46, 53, 60, 67, 74, 81, 88, 95], 5=>[5, 12, 19, 26, 33, 40, 47, 54, 61, 68, 75, 82, 89, 96], 6=>[6, 13, 20, 27, 34, 41, 48, 55, 62, 69, 76, 83, 90, 97], 0=>[7, 14, 21, 28, 35, 42, 49, 56, 63, 70, 77, 84, 91, 98]}

first

引数を指定して先頭から要素を指定された分だけとっていく。 takeがエイリアスであるが、firstの場合のみ引数を指定しないで、最初の要素だけ取得することが可能。

drop

takeと対になる。(つまり必ず引数を指定する。) 先頭から指定した数だけ要素を切り落とす。(shiftの連続版だが、shiftは破壊的。)

take_while

ブロックを渡す。最初から順番にブロックで評価していき、ブロックの評価が偽になるまでを返す。

irb(main):035:0> station = %w|tokyo shinagawa shinyokohama odawara atami mishima shinfuji shizuoka|
irb(main):047:0> station.take_while{|e| e != "atami" }
=> ["tokyo", "shinagawa", "shinyokohama", "odawara"]

熱海まではブロックの評価がtrueであったが、熱海でfalseとなったため、東京から小田原までが表示されている。

drop_while

take_whileと対になる。最初から順番にブロックで評価していき、ブロックの評価が真になるまでを返す。

dropだとわかりにくいが、最初に真になった 要素なので、以下のように使える。

irb(main):048:0> station.drop_while{|e| e != "atami" }
=> ["atami", "mishima", "shinfuji", "shizuoka"]

熱海までは評価が真だったが、熱海で初めて評価が真となったため、そこまでが切り落とされている。