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..40は20..30を完全に内包しています。
逆に部分集合とは、別の集合の一部から構成される集合のことです:
Set.new(25..27) <= Set.new(20..30)
SortedSet(ソート済み集合)の使い方
常にソートされた状態を保ちたい集合が必要な場合は、SortedSetクラスを使用できます。
このクラスを使用するには、いくつかの条件があります:
- 追加するオブジェクトは spaceship 演算子(<=>)を実装している必要があります。
- オブジェクト同士が互いに比較可能である必要があります(整数同士、文字列同士など)。
以下は使用例です:
sorted_numbers = SortedSet.new
sorted_numbers << 5
sorted_numbers << 2
sorted_numbers << 1
sorted_numbers
# SortedSet: {1, 2, 5}
要素をどの順番で追加しても、自動的に昇順で並び替えられるのがわかります。
まとめ
この記事では、より高いパフォーマンスとシンプルなコードのためにRubyでSetを使う方法を学びました。また、配列とSetの違いについても理解できたはずです。
特に、重複排除と高速な要素検索が求められる場面では、Setは非常に強力な選択肢となります。ぜひ実際のコードで試してみてください。
最後までお読みいただきありがとうございました!
-
Rubyメソッドをスパイする方法!TracePointで実行をトレースしよう
Rubyには、TracePointクラスを使ってアクセスできる組み込みのトレーシングシステムが備わっています。このシステムを利用すると、メソッド呼び出し、新しいスレッドの開始、例外の発生などをトレースできます。 なぜこれを使いたくなるのでしょうか? たとえば、特定のメソッドの実行過程を追跡したい場合に非常に便利です。その処理の中で他にどのメソッドが呼ばれているのか、どんな戻り値が返されているのかを、目で確認できるようになります。 それでは、いくつか具体例を見ていきましょう! メソッド呼び出しをトレースする 多くの場合、TracePointでトレースしたいのはアプリケーション側のコードであり、
-
Rubyの配列(Array)クラスの使い方徹底解説!基本操作と便利なメソッド一覧
配列(Array)とは? 配列はRubyに組み込まれたクラスで、0個以上の要素をリスト形式で保持することができます。さらに、要素の追加・アクセス・繰り返し処理を簡単に行えるメソッドも多数用意されています。 もし配列が存在しなければ、値ごとに個別の変数を用意しなければならず、管理が非常に大変になります。 例: a = 1 b = 2 c = 3 しかし、配列を使えば次のように書けます: numbers = [1, 2, 3] さらに嬉しいことに、配列の中には何でも入れられます! 例えば: 数値 文字列 さらに別の配列!(これは多次元配列になります) それでは、配列を最大限に活用できる