lambda consulting

Enumerable で一気にドンッ!!

July 08, 2015

Enumerable

RubyEnuerble モジュールについて。

mapreduce など、 Enumerable モジュールのメソッドは、それをincludeしているコレクションが用意している each で順次処理している(コレクションとは、配列やハッシュのこと)。

けど、コードの見た目上は、ループされているイメージではなく、「一気にドンッ!!」と処理しているように表現できる。

同じ結果を得るのに、5行で書いていたところを、1行で書けるというのは、善だ。なぜなら、視界に多くが納まることは、思考を助ける。

問題は、構文を知っているか、知らないかだけ。なので、知っておいた方がいい Enumerable のメソッドをまとめる。

1. それぞれの要素に、同じ作用を与える

コレクションに対して、それぞれの要素に与えたい作用をブロックで与えると、その結果を配列で得られるメソッド。
ちなみ、Enuerble のメソッドには、同じことをするが、異なる名前のものがいくつか存在する。これは、Smalltalk の流れを引き継いだか、Lisp の流れを引き継いだかによるもの。
この場合、Smalltalk の流れを引き継いだのが、collectLisp の流れを引き継いだのが、map

[1, 2, 3].collect {|x| x + 1}   # => [2, 3, 4]
[1, 2, 3].map {|x| x + 1}   # => [2, 3, 4]

2. 全ての要素をまとめて、ひとつの結果を得る

コレクションに対して、それぞれの要素をとりまとめるルールをブロックで与えると、その結果をスカラー(単一の値)で得るメソッド。
同じく、2つの名前があり、Smalltalk の流れを引き継いだのが、injectLisp の流れを引き継いだのが、reduce

[1, 2, 3].inject {|sum, n| sum + n}   # => 6
[1, 2, 3].reduce {|sum, n| sum + n}   # => 6

# 単純な演算であれば、略記できる
[1, 2, 3].reduce(:+)  # => 6

# メソッドへの引数として、初期値を与えられる
[1, 2, 3].reduce(10) {|sum, n| sum + n}   # => 16

3. 特定の条件に合致するもののみを抽出する

コレクションの中から、抽出したい条件をブロックで与えると、合致するものを返す。
これも名前が2つあるが、コレクションがハッシュの場合の結果が異なる。

scores = { 'Alice' => 50, 'Bob' => 60, 'Carol' => 90, 'David' => 40 }

scores.find_all {|k, v| v >= 60 }  # => [["Bob", 60], ["Carol", 90]]

scores.select {|k, v| v >= 60 } # => {"Bob"=>60, "Carol"=>90}

4. 正規表現にマッチする要素に対して、同じ作用を与える

a = ["foo", "bar", "baz"]

# bで始まるものを得る
a.grep(/^b/)  # => ["bar", "baz"]

# bで始まるものの文字数を得る
a.grep(/^b/) {|x| x.size}  # => [3, 3]

5. 全ての要素が条件に合致するか? / 条件に合致する要素が1つでも含まれるか?

# 全ての要素が条件に合致するか?
[2, 4, 6].all? {|x| x % 2 == 0}  # => true

# 条件に合致する要素が1つでも含まれるか?
[3, 5, 6].any? {|x| x % 2 == 0}  # => true

6. 最大/最小の要素を得る

scores = { 'Alice' => 50, 'Bob' => 60, 'Carol' => 90, 'David' => 40 }

# キーの最大
scores.max  # => ["David", 40]

# (これも)キーの最大
scores.max {|x, y| x[0] <=> y[0]}  # => ["David", 40]

# 値の最大
scores.max {|x, y| x[1] <=> y[1]}  # => ["Carol", 90]

7. 要素を並べ替える

コレクションを並び替える。ブロックの適用の仕方により、次の2つがある。

a = [7, 6, 5, 4, 3, 2, 1]

# 全て同じ結果
a.sort  #=> [1, 2, 3, 4, 5, 6, 7]
a.sort {|x, y| x <=> y}  #=> [1, 2, 3, 4, 5, 6, 7]
a.sort_by  #=> [1, 2, 3, 4, 5, 6, 7]
a.sort_by {|x| x}  #=> [1, 2, 3, 4, 5, 6, 7]


# 逆順で並べる
a.reverse  #=> [1, 2, 3, 4, 5, 6, 7]


h = { 3 => "a",1 => "b", 2 => "c" }

# ハッシュをキーで並び替える
h.sort_by {|k, v| v}  # => [{3=>"a", 1=>"b", 2=>"c"}]
# ハッシュを値で並び替える
h.sort_by {|k, v| k}  # => [{3=>"a", 1=>"b", 2=>"c"}]