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

RubyのSet(集合)クラス徹底解説 – 基本の使い方から演算子・主要メソッドまで

RubyのSet(集合)とは何でしょうか?

Setは、配列のように要素を格納できるクラスです。

しかし、特定の場面では配列より10倍速く動作する特別な性質を持っています。

さらに重要なポイントとして:

Setに含まれるすべての要素は必ず一意(ユニーク)であることが保証されます

このチュートリアルでは、以下の内容を学べます:

  • Setを最大限に活用する方法と、使うべきタイミング
  • Setと配列の違い
  • 便利なSetメソッドの一覧

それでは始めましょう!

RubyのSetの基本例

Setは、重複のない要素のリストを作成するためのRubyクラスです。

これがどのように役立つか、具体例を見てみましょう。

例えば、大量の商品リストを処理しているとします。

しかし、そのリストには重複したデータが含まれており、ユニークな商品だけを取得したいとしましょう。

そんなときはSetに要素を追加するだけで、Setが自動的に重複を排除し、余計な手間なしに常にユニークな商品リストを維持してくれます。

実際のコードは次のようになります:

require 'set'

products = Set.new

products << 1
products << 1
products << 2

products
# Set: {1, 2}

もうひとつのメリットは、このリストからの検索が非常に高速であることです:

products.include?(1)

# true

この検索が高速な理由は、定数時間(O(1))で検索が行われるためです。データ量が増えても検索速度はほぼ変わりません。

Setと配列の違いを理解する

ここで疑問に思うかもしれません…

Setと配列には一体どんな違いがあるのでしょうか?

Setには、インデックスによる要素への直接アクセス機能がありません:

products[0]

# undefined method `[]'

これが主な違いです。

ただし、必要になればSetはいつでも配列に変換できます

products.to_a

# [1, 2]

Setを使う最大の目的は、以下の2つの特別な性質を活かすことにあります:

  • include?による高速な検索
  • 値の重複がないことが保証される

この2つが必要であれば、Setを使うことでパフォーマンスが大幅に向上します。さらに、ユニークな要素が欲しいときに毎回配列に対してuniqを呼び出す必要もなくなります。

Setと配列のベンチマーク比較

次のベンチマークは、配列とSetのinclude?メソッドのパフォーマンス差を示しています。

# Ruby 2.5.0

set   include: 8381985.2 i/s
array include: 703305.5  i/s - 11.92x  slower

この差が生まれる理由は、配列はすべての要素を1つずつチェックしなければならないからです。

100万個の要素を持つ配列の場合、include?を呼び出すたびに最大100万個の要素を確認することになります。

一方、Setにはその必要がありません。内部的にハッシュ構造を利用しているため、要素数に関わらず一定の速度で検索できます。

RubyのSetメソッド

Set同士の間では、さまざまな演算やメソッドを実行できます。数学の知識がある方は、見覚えのある操作だと気づくかもしれません。

和集合演算子(Union)

便利なSetメソッドのひとつが和集合演算子です:

products | (1..10)

# Set: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

この演算子を使うと、2つの集合のユニークな要素をすべて1つの集合に結合できます。

ちなみに、これはSetクラスのオブジェクトだけでなく、配列・範囲(Range)・ハッシュなど、あらゆるEnumerableオブジェクトに対して動作します。

差集合演算子(Difference)

ある集合から別の集合の要素を取り除きたい場合は、この演算子を使用します。

例を見てみましょう

products - (3..4)

# Set: {1, 2, 5, 6, 7, 8, 9, 10}

積集合演算子(Intersection)

そしてこちらが積集合演算子です:

Set.new(1..3) & Set.new(2..5)

# Set: {2, 3}

これは両方の集合に共通して存在する要素を返します。

これら3つの演算子(和集合・差集合・積集合)は、配列に対しても同じように使用できます。

上位集合(Superset)と部分集合(Subset)

上位集合とは、別の集合のすべての要素を含む集合のことです。

ある集合が別の集合の上位集合であるかどうかを判定できます。

次のようなコードです

Set.new(10..40) >= Set.new(20..30)

範囲10..4020..30を完全に内包しています。

逆に部分集合とは、別の集合の一部から構成される集合のことです:

Set.new(25..27) <= Set.new(20..30)

SortedSet(ソート済み集合)の使い方

常にソートされた状態を保ちたい集合が必要な場合は、SortedSetクラスを使用できます。

このクラスを使用するには、いくつかの条件があります:

  1. 追加するオブジェクトは spaceship 演算子(<=>)を実装している必要があります
  2. オブジェクト同士が互いに比較可能である必要があります(整数同士、文字列同士など)。

以下は使用例です:

sorted_numbers = SortedSet.new

sorted_numbers << 5
sorted_numbers << 2
sorted_numbers << 1

sorted_numbers
# SortedSet: {1, 2, 5}

要素をどの順番で追加しても、自動的に昇順で並び替えられるのがわかります。

まとめ

この記事では、より高いパフォーマンスとシンプルなコードのためにRubyでSetを使う方法を学びました。また、配列とSetの違いについても理解できたはずです。

特に、重複排除と高速な要素検索が求められる場面では、Setは非常に強力な選択肢となります。ぜひ実際のコードで試してみてください。

最後までお読みいただきありがとうございました!

  1. Rubyメソッドをスパイする方法!TracePointで実行をトレースしよう

    Rubyには、TracePointクラスを使ってアクセスできる組み込みのトレーシングシステムが備わっています。このシステムを利用すると、メソッド呼び出し、新しいスレッドの開始、例外の発生などをトレースできます。 なぜこれを使いたくなるのでしょうか? たとえば、特定のメソッドの実行過程を追跡したい場合に非常に便利です。その処理の中で他にどのメソッドが呼ばれているのか、どんな戻り値が返されているのかを、目で確認できるようになります。 それでは、いくつか具体例を見ていきましょう! メソッド呼び出しをトレースする 多くの場合、TracePointでトレースしたいのはアプリケーション側のコードであり、

  2. Rubyの配列(Array)クラスの使い方徹底解説!基本操作と便利なメソッド一覧

    配列(Array)とは? 配列はRubyに組み込まれたクラスで、0個以上の要素をリスト形式で保持することができます。さらに、要素の追加・アクセス・繰り返し処理を簡単に行えるメソッドも多数用意されています。 もし配列が存在しなければ、値ごとに個別の変数を用意しなければならず、管理が非常に大変になります。 例: a = 1 b = 2 c = 3 しかし、配列を使えば次のように書けます: numbers = [1, 2, 3] さらに嬉しいことに、配列の中には何でも入れられます! 例えば: 数値 文字列 さらに別の配列!(これは多次元配列になります) それでは、配列を最大限に活用できる