Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

RubyのEnumerableモジュール入門ガイド〜筆者イチオシのeach_consメソッドを徹底解説〜

Enumerableモジュールとは?

Enumerableは繰り返し処理(イテレーション)用メソッドのコレクションで構成されるRubyのモジュールであり、Rubyが多くの開発者に愛される大きな理由のひとつとなっています。

Enumerableには、次のような便利なメソッドが含まれています

  • map
  • select
  • inject

Enumerableのメソッドは、ブロックを渡すことで動作します。

ブロックの中には、各要素に対して実行したい処理を記述します。

例えば

[1,2,3].map { |n| n * 2 }

このコードを実行すると、すべての数値が2倍になった新しい配列が得られます。

実際の挙動は使用するメソッドによって異なります。mapはすべての値を変換するためのもので、selectはリストのフィルタリングに、injectは配列内の値を合計するために使えます。

RubyのEnumerableには20以上のメソッドが用意されています。

それでは、その中のひとつを詳しく見ていきましょう。

each_consメソッドの使い方

私が最近特に気に入っているEnumerableメソッドはeach_consです!

その理由はこちら

このメソッドはとても実用的で、n-gram(連続するn個の要素)の抽出や、別のEnumerableメソッドであるall?と組み合わせて「数列が連続しているかどうか」の判定などに活用できます。

RubyのEnumerableモジュール入門ガイド〜筆者イチオシのeach_consメソッドを徹底解説〜

each_consはサイズnの部分配列を順番に返します。たとえば[1,2,3]に対してeach_cons(2)を呼び出すと、[[1,2], [2,3]]が得られます。

具体例を見てみましょう

numbers = [3,5,4,2]

numbers.sort.each_cons(2).all? { |x,y| x == y - 1 }

このコードでは、まずsortで数値を並べ替え、続いてeach_cons(2)を呼び出しています。この戻り値はEnumeratorオブジェクトで、最後にall?メソッドを使って、すべての要素が条件を満たすかどうかをチェックしています。

もうひとつ例を挙げます。今度はeach_consを使って、「ある文字が同じ文字に挟まれているか」(xyxのようなパターン)を判定してみましょう。

str = 'abcxyx'

str.chars.each_cons(3).any? { |a,b,c| a == c }

さらに便利なテクニックもあります!

このパターンが何回出現するかを知りたい場合は、true/falseを返すany?countに置き換えるだけでOKです。

個人的にさらに興味深いのは、each_consメソッドの内部実装です。

array = []

each do |element|
  array << element
  array.shift     if array.size > n
  yield array.dup if array.size == n
end

注記:このコードはRubiniusによるEnumerableの実装から引用したものです。元のソースコードは公式リポジトリで確認できます。

実装の流れを見ていきましょう。まず空のRuby配列を用意し、eachで各要素を順番に処理していきます。

ここまではごく標準的な処理です。しかし次に、要素を配列に追加し、配列のサイズが指定サイズ(each_consの引数)を超えていたらArray#shiftで先頭の要素を取り除いて調整します。

そして、配列が要求されたサイズちょうどになったタイミングで、その配列のdup(複製)をyieldします。

これはまさに天才的な設計だと思います。配列のインデックスを細かく操作することなく、「スライディングウィンドウ(スライド窓)」のような効果を実現しているからです。

その他の覚えておきたいEnumerableメソッド

メソッド説明
count名前の通り、ブロック内でtrueと評価される要素の数を数えます
group_byブロックの戻り値をキーとして要素をグループ化します。ハッシュを返します
partition要素を2つのグループに分割します。二次元配列を返します
any?ブロックが渡された要素のうち、どれか1つでもtrueを返せばtrueを返します
all?ブロックがすべての要素に対してtrueを返せばtrueを返します
none?all?の逆で、すべての要素が条件を満たさなければtrueを返します
cycle(n)すべての要素をn回繰り返します。[1,2].cycle(2)なら[1,2,1,2]のようになります
findselectに似ていますが、条件に合う最初の1件だけを返します
inject前のブロックの結果を次のブロックに引き渡しながら畳み込みます。合計値の計算などに便利です
zip2つのenumerableオブジェクトを束ねて並行して扱えるようにします。要素の比較やハッシュ生成に役立ちます
mapenumerableオブジェクトの各要素を変換し、新しい配列として返します

まとめ

この記事で見てきたように、Enumerableは習得する価値のあるモジュールです。ぜひ公式ドキュメントにも目を通して、自分のコードで何ができるのか試してみてください!

この記事が役に立ったら、ぜひシェアやニュースレターの登録をお願いします。大きな励みになります!🙂

  1. Rubyのmapメソッド完全ガイド!配列・ハッシュのデータ変換を実例で解説

    Rubyのmapメソッドは、配列(Array)・ハッシュ(Hash)・範囲(Range)といったコレクションに対して使用できる強力なメソッドです。 mapの主な用途は、データの変換です。 例えば、文字列の配列があった場合、すべての文字列を順番に処理して、各文字を大文字に変換することができます。 また、Userオブジェクトのリストがある場合も同様です。 それらを変換して、対応するメールアドレスや電話番号など、Userクラスに定義された任意の属性のリストを作成できます。 それでは、具体的な使い方を見ていきましょう! Ruby mapメソッドの基本構文 mapの構文は次のようになっています。 a

  2. Ruby開発者のための時間計算量とBig O記法 徹底解説ガイド

    時間計算量は、コンピュータサイエンスの中でも最も興味深い概念のひとつです。しかも、特別な学位がなくても十分に理解できます。 この概念が面白いのは、あるアルゴリズムやプログラムがなぜ遅いのかを見抜き、どうすれば高速化できるのかを明確にしてくれる点にあります。 もちろん、自分が書いたコードにもすぐに応用できます。 さらに、教科書に載っているような高度なアルゴリズムだけではなく、日常的に書くコードにも役立つのがポイントです。本記事の後半で実際に確認していきます。 まずは、「遅い」と「速い」の定義から整理しましょう。 「遅い」と「速い」の定義 100万個の数字を150ミリ秒(ms)でソートするのは、果