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

Rubyの演算子を徹底解説!知っておくべき全種類と実践的な使い方

Rubyには、他の言語では見られないような興味深い演算子が数多く用意されています。

たとえば、以下のようなものです。

  • 宇宙船演算子(<=>
  • 剰余代入演算子(%=
  • 三重等号(===)演算子
  • 大なり(>)・小なり(<
  • 不等号(!=

しかし、意外と知られていないのが、これらの演算子の多くが実はRubyのメソッドだという点です。

つまりどういうことかというと…

演算子の動作を上書きして、自分のクラスに独自の振る舞いを定義できるのです。

たとえば==を定義すれば、「同じクラスの2つのオブジェクトをどのように比較するか」をRubyに教えることができます。

それでは、いくつかの例を見ながら、Rubyの各演算子がどのように動作し、コードの中でどう使えばよいのかをしっかり理解していきましょう。

Rubyを本当に理解したいなら、これは避けて通れない重要なテーマです。

目次

  • 1. Rubyの論理比較演算子
  • 2. Rubyの算術演算子
  • 3. 代入演算子(=、+=、||=)
  • 4. 単項演算子とは?
  • 5. スプラット演算子(*)とその例
  • 6. マッチング演算子(=~)
  • 7. 三項演算子(条件演算子)
  • 8. ショベル/プッシュ演算子(<<)
  • 9. 三重等号演算子(===)は等価性以上のもの
  • 10. セーフナビゲーション演算子(&.)
  • 11. 演算子の優先順位表
  • 12. まとめ

Rubyの論理比較演算子

まずは論理比較演算子から見ていきましょう。

これらの演算子を使うと、2つのオブジェクトを比較し、その結果に基づいて判断を下すことができます。

一覧はこちらです。

演算子 説明
< より小さい
> より大きい
>= 以上
<= 以下
== 等しい
!= 等しくない
<=> 大きい・等しい・小さいを判定

これらの演算子はすべてメソッドであり、真偽値(boolean)を返します。ただし、唯一の例外が宇宙船演算子(<=>)で、こちらは「1(大きい)」「0(等しい)」「-1(小さい)」のいずれかを返します。

>演算子の使用例はこちらです。

if orange.stock > 20
  # ...
end

自作クラスで等価演算子(==)を使おうとしても、最初はうまく動作しないことがあります。

class Fruit
 def initialize(name)
  @name = name
 end
end

orange1 = Fruit.new("orange")
orange2 = Fruit.new("orange")

orange1 == orange2
# false

この理由は、==のデフォルト実装であるBasicObject#==が、object_idメソッドを使って「2つのオブジェクトが同一のインスタンスかどうか」を判定しているためです。

これは次のように修正できます。

class Fruit
  attr_reader :name

  def initialize(name)
    @name = name
  end

  def ==(other)
    name == other.name
  end
end

ここでは「2つの果物が同じである」とは何を意味するのかを定義しています。それは名前が同じであることですね。

Rubyの算術演算子

次は算術演算子です。

ここは特に新しいことはありませんね。

5 + 5
# 10

10 * 2
# 20

10 ** 2
# 100

しかし、==演算子と同じように、これらもメソッドです。

このことは非常に便利です。「2つのオブジェクトを足し合わせる」とは何を意味するのかを自分で定義できるからです。

たとえば、2つのOrderオブジェクトがあるとき、足し合わせると支払総額になったり、両方を組み合わせた新しい注文オブジェクトになったりする、といった具合です。

+メソッドを定義することで、その動作を自由に決められるのです。

もうひとつ、馴染みがないかもしれない演算子が剰余演算子(モジュロ)です。

パーセント記号(%)のような見た目をしていて、割り算の余りを求める働きがあります。

10 % 2
# 0

剰余演算子には多くの実用的な用途があります。数値が偶数か奇数かの判定、ある数が別の数で割り切れるかの確認、数値の上限設定などに使えます。

代入演算子(=、+=、||=)

次は代入演算子です。ここまで見てきた演算子と違い、代入演算子はメソッドではありません。

基本となる代入演算子はこちら。

a = 1

さらに、複合代入演算子もあります。

a += 5
# 6

a *= 2
# 12

これらは「現在の値を読み取り、算術演算子で計算し、結果を保存する」ことと同等です。剰余演算子(%)を含むすべての算術演算子でこの形式が使えます。

しかし、動作が少し特殊な代入演算子が2つあります!

それが||=&&=です。

この2つは、算術演算子版と単純には等価ではないためです。

a ||= 100が行うのは、次のような処理です。

「もしaが存在しないか、falseまたはnilであれば100を代入する。そうでなければ、単にaの値を返す」

最も近い等価表現を書くと、こうなります。

(defined?(a) && a) ? a : a = 100

この性質は、時間のかかる計算やAPIリクエストの結果を保存しておきたい場合に便利です。この手法は「メモ化(memoization)」として知られています。

単項演算子とは?

これまで見てきた演算子は2つの値を扱うものでしたが、1つの値だけを扱う演算子も存在します。これらは「単項演算子」と呼ばれます。

たとえば

+"abc"

これは、freezeされた文字列のミュータブル(変更可能)なコピーを作成します。

独自の単項演算子(+ / -)を定義することもできますが、その場合は特別な構文が必要です。

class String
  def +@
    frozen? ? self.dup : self
  end
end

str = "abc".freeze

p (+str).frozen?
# false

単項演算子の優先順位の関係で、ここでは括弧を使う必要があります。

また、!!という演算子もありますが、これはメソッドではありません。

!!123
# true

!!nil
# false

この演算子はどんな値でも真偽値に変換できるので便利です。

さらに!もあります。こちらは同じように真偽値へ変換しますが、反対の値を返します。

!true
# false

!!true
# true

!false
# true

Rubyのスプラット演算子(*)とその例

スプラット演算子(*)は興味深い存在です。これがなければ実現できないことを可能にしてくれるからです。

たとえば、次のような配列があるとします。

attributes = [:title, :author, :category]

そして、可変長引数を受け取るメソッド(attr_readerなど)に、この配列を渡したいとします。

その場合はこう書けます。

attr_reader *attributes

スプラット演算子は、配列をその要素のリストへと変換します。つまり、配列を取り除いて、中身だけを展開した状態にするイメージです。

言い換えると、先ほどの例は次のコードと同じ意味になります。

attr_reader :title, :author, :category

これがスプラット演算子の強力さです。

マッチング演算子(=~)

チルダ(~)が付いた、この奇妙な見た目のRuby演算子(=~)は何なのでしょう?

これはマッチング演算子です!

正規表現を使った手軽な検索を行い、マッチした位置(インデックス)を取得できます。

例を見てみましょう。

"3oranges" =~ /[0-9]/
# 0

これは数字を探し、最初にマッチした位置のインデックスを文字列内で返します。マッチしなかった場合はnilを返します。

さらに、!~という「NOTマッチ」演算子もあります。

"abc" !~ /[0-9]/
# false

こちらはインデックスではなく、truefalseを返す点に注意してください。

Rubyの三項演算子(条件演算子)

コンパクトで短いコードがお好みなら、Rubyの三項演算子はきっと気に入るはずです。

これは、if/else文を簡潔に書くための方法です。

書式は次のとおり

condition ? true : false

具体例

"".size == 0 ? "Empty string" : "Non-empty string"

仕組みはこうなっています

三項演算子の最初の部分で条件を定義します("".size == 0)。

続いて疑問符(?)がきます。

その後ろには、条件がtrueの場合の戻り値を書きます。

そしてコロン(:)。

最後の部分は、条件がfalseの場合の戻り値です。通常の条件分岐におけるelseに相当します。

ショベル/プッシュ演算子(<<)

この演算子(<<)もメソッドの一種なので、対象となるオブジェクトによって動作が変わります。

たとえば配列の場合、単純にpushメソッドの別名(エイリアス)です。

animals = []

animals << "cat"

文字列の場合は、末尾に追加します。

"" << "cat"

そして整数(Integer)の場合は「左シフト」、つまりすべてのビットを左に回転させる操作になります。

2 << 1
# 4

2 << 2
# 8

2 << 3
# 16

三重等号演算子(===)は等価性以上のもの

本日の最後の演算子は、三重等号演算子(===)です。

これもメソッドであり、予想外の場所で登場することもあります。

たとえば、case文の中です。

case "bacon"
when String
  puts "It's a string!"
when Integer
  puts "It's an integer"
end

ここでRubyは、クラスに対して===メソッドを呼び出しています。

具体的には次のような呼び出しです。

String === "bacon"

これは、現在のクラスと相手のオブジェクトのクラスを比較します。

つまりこの演算子の目的は、case文の文脈における「等価性」を定義することなのです。

セーフナビゲーション演算子(&.)

オブジェクトに対してメソッドを呼び出したいけれど、そのオブジェクトがnilかもしれない——そんな場面はよくあります。nilに対してメソッドを呼ぶとエラーになることが多いので、これは困りますよね。

ひとつの解決策はこちら。

if user && user.active
  # ...
end

もっと良い書き方はこうです。

if user&.active
  # ...
end

この&.がセーフナビゲーション演算子(Ruby 2.3で導入)で、usernilでない場合にのみactiveメソッドを呼び出します。

とても便利ですね!

演算子の優先順位表

Rubyは、数学における掛け算や括弧の扱いと同じように、優先順位のリストに従ってソースコードを評価します。

この仕組みを理解していないと、あらゆる種類のバグの原因になりかねません。

優先順位の高い順から低い順への表がこちら

演算子
!, ~, 単項 +
**
単項 -
*, /, %
+, -
<<, >>
&
|, ^
>, >=, <, <=
<=>, ==, ===, !=, =~, !~
&&
||
?, :
修飾rescue(modifier-rescue)
=, +=, -=, *=, /=, %=
defined?
not
or, and
修飾if / 修飾unless / 修飾while / 修飾until
{ } ブロック
do ... end ブロック

「修飾〜(modifier-something)」とは、これらのキーワードを1行で書く記法のことです。

puts "awesome" if blog_name == "rubyguides"

ブロックの優先順位が思わぬ挙動を引き起こす例も見てみましょう。

# 大文字に変換された文字の配列を返す
p ["a", "b", "c"].map { |character| character.upcase }

# Enumeratorオブジェクトを返す
p ["a", "b", "c"].map do |character|
  character.upcase
end

最初のケースは期待どおりに動作しますが、2番目のケースではブロックの優先順位が低いため、mapはブロックが渡されていないと判断し、Enumeratorを返してしまうのです。

まとめ

今回は、算術演算子から論理演算子、そしてややマニアックな単項演算子まで、Rubyの多彩な演算子について学びました。

これらの演算子の多くは実際にはメソッドであり、自分のクラス内で独自に実装できることもわかりましたね。

この記事が皆さんのお役に立てば幸いです!

最後まで読んでいただき、ありがとうございました。


  1. Bluetooth 5とは?知っておきたい新機能と特徴を徹底解説

    Wi-Fiと同様に、Bluetoothはスマートフォンをはじめとするあらゆるモバイルデバイスに欠かせない技術です。ファイルやデータの転送だけでなく、ワイヤレススピーカーやスポーツ用イヤホンなどの周辺機器との接続にも幅広く活用されています。 Bluetoothはコミュニケーションのあり方を変えただけでなく、多くのデバイスを大型からコンパクトへと進化させました。最新バージョンのBluetooth 5では、従来のバージョンでは実現できなかったさまざまなことが可能になっています。劇的な変革とまでは言えませんが、確かに新しい魅力がいくつも追加されています。それでは、Bluetooth 5がもたらす新機能

  2. PBMファイルとは?正体や開き方・変換方法をわかりやすく解説

    パソコンが身近な存在になって以来、テクノロジーとエンターテインメントの分野は目覚ましい成長を遂げてきました。その中でも最も一般的な娯楽のひとつが、写真をはじめとするメディアコンテンツです。現在の技術では、特定のファイルを開くために対応アプリケーションを探す手間はほとんどありません。しかし、画像やメディアファイルにはそれぞれ異なる種類があり、多様な拡張子が存在することを知っておくことは有益です。そのひとつである「PBM」という拡張子は、ここしばらくユーザーの間で注目を集めています。本記事では、PBMファイルの概要とその扱い方についてわかりやすくご紹介します。 PBMファイルとは? PBMは「P