演算子(Ruby)
『Ruby』の演算子は、算術・比較・論理の3種類が基本です。Rubyでは多くの演算子がメソッドとして実装されており、クラスに応じた動作の変更(演算子オーバーロード)も可能です。
算術演算子
# 基本的な算術演算子です。 a + b # 加算 a - b # 減算 a * b # 乗算 a / b # 除算(整数同士の場合は商のみ返します) a % b # 剰余(余り) a ** b # 冪乗(べき乗) # divmod: 商と余りを配列で同時に返します。 a.divmod(b) # => [商, 余り]
比較演算子
# 同値比較です。 a == b # 値が等しければ true を返します。 a != b # 値が等しくなければ true を返します。 # 宇宙船演算子(Comparable で使われます)。 a <=> b # a が小さければ -1、等しければ 0、大きければ 1 を返します。 # 同一性比較です(オブジェクトIDで比較します)。 a.equal?(b) # 同じオブジェクトかどうかを確認します。 # 型と値の両方を比較します。 a.eql?(b) # 型と値が両方等しければ true を返します。
論理演算子
# 記号形式(優先度が高く、条件式に適しています)。 a && b # AND: 両方が真のとき true を返します。 a || b # OR: どちらかが真のとき true を返します。 !a # NOT: 真偽を反転します。 # 英語形式(優先度が低く、制御フローに適しています)。 a and b # AND(&& より優先度が低い) a or b # OR(|| より優先度が低い) not a # NOT(! より優先度が低い)
演算子一覧
算術演算子
| 演算子 / メソッド | 概要 |
|---|---|
| + | 加算します。文字列や配列の結合にも使えます。 |
| - | 減算します。 |
| * | 乗算します。文字列や配列の繰り返しにも使えます。 |
| / | 除算します。整数同士の場合は小数点以下を切り捨てた商を返します。 |
| % | 剰余(余り)を返します。 |
| ** | 冪乗を計算します。 |
| divmod(n) | 商と余りを配列『[商, 余り]』で同時に返します。 |
比較演算子
| 演算子 / メソッド | 概要 |
|---|---|
| == | 値が等しければ『true』を返します。型が違っても値が同じなら等しいと判定されることがあります。 |
| != | 値が等しくなければ『true』を返します。 |
| <=> | 宇宙船演算子。左が小さければ『-1』、等しければ『0』、大きければ『1』を返します。 |
| eql?(other) | 型と値が両方等しいときのみ『true』を返します。ハッシュのキー比較に使われます。 |
| equal?(other) | 同じオブジェクト(同じobject_id)のときのみ『true』を返します。 |
論理演算子
| 演算子 | 概要 |
|---|---|
| && | AND演算子です。両辺が真のとき『true』を返します。左辺が偽なら右辺は評価しません(短絡評価)。 |
| || | OR演算子です。どちらかが真のとき『true』を返します。左辺が真なら右辺は評価しません(短絡評価)。 |
| ! | NOT演算子です。真偽を反転します。 |
| and | 『&&』と同じですが優先度が低く、制御フロー(メソッド呼び出しの後)で使われます。 |
| or | 『||』と同じですが優先度が低く、エラー時の代替処理などで使われます。 |
| not | 『!』と同じですが優先度が低く、読みやすさを重視する場面で使われます。 |
サンプルコード
算術演算子と『divmod』の基本的な使い方です。KOFキャラクターの戦闘力を計算します。
arithmetic_operators.rb
# KOFキャラクターの戦闘力(power)と体力(hp)を定義します。
terry_power = 9500 # テリー・ボガードの戦闘力
kyo_power = 9800 # 草薙京の戦闘力
terry_hp = 1000 # テリーの体力
# 加算・減算・乗算・除算の基本操作です。
puts terry_power + kyo_power # => 19300(合計戦闘力)
puts kyo_power - terry_power # => 300(差分)
puts terry_power * 2 # => 19000(2倍強化)
puts terry_power / 3 # => 3166(整数除算、小数点以下は切り捨て)
# 剰余(%)で3で割り切れるか確認します。
puts terry_power % 3 # => 2(余り)
# 冪乗(**)で攻撃力ボーナスを計算します。
combo_bonus = 2 ** 5
puts "コンボボーナス: #{combo_bonus}" # => 32
# divmod で体力の消費を商と余りに分解します。
# 1000の体力を300ずつ削ったとき、何回削れてどれだけ余るか計算します。
quotient, remainder = terry_hp.divmod(300)
puts "削れた回数: #{quotient}、残りHP: #{remainder}" # => 3、100
実行すると次のように出力されます。
ruby arithmetic_operators.rb 19300 300 19000 3166 2 コンボボーナス: 32 削れた回数: 3、残りHP: 100
比較演算子の違いを確認します。『==』『eql?』『equal?』の挙動の差を検証します。
comparison_operators.rb
# == は値の等価性を比較します。
puts 1 == 1.0 # => true(値が等しい)
puts 1.eql?(1.0) # => false(型が違うため false)
# equal? は同一オブジェクトかを比較します。
str_a = "テリー・ボガード"
str_b = "テリー・ボガード"
str_c = str_a # 同じオブジェクトを参照させます。
puts str_a == str_b # => true(値が等しい)
puts str_a.eql?(str_b) # => true(型も値も等しい)
puts str_a.equal?(str_b) # => false(別オブジェクト)
puts str_a.equal?(str_c) # => true(同じオブジェクト)
# <=> 宇宙船演算子で大小を比較します。
powers = { terry: 9500, kyo: 9800, iori: 9600, mai: 8900, rock: 9200 }
puts powers[:kyo] <=> powers[:terry] # => 1(kyo の方が大きい)
puts powers[:terry] <=> powers[:kyo] # => -1(terry の方が小さい)
puts powers[:terry] <=> 9500 # => 0(等しい)
# sort は <=> を使って並べ替えます。
sorted = powers.sort_by { |_name, p| -p }
sorted.each { |name, p| puts "#{name}: #{p}" }
実行すると次のように出力されます。
ruby comparison_operators.rb true false true true false true 1 -1 0 kyo: 9800 iori: 9600 terry: 9500 rock: 9200 mai: 8900
論理演算子の短絡評価と、記号形式と英語形式の優先度の違いを確認します。
logical_operators.rb
# 短絡評価(ショートサーキット)の動作を確認します。
# && は左辺が false/nil なら右辺を評価しません。
def attack(name)
puts "#{name}が攻撃しました。"
true
end
# テリーが戦闘可能(true)なので、右辺も評価されます。
terry_ready = true
terry_ready && attack("テリー・ボガード") # => "テリー・ボガードが攻撃しました。"
# 草薙京が気絶中(false)なので、右辺は評価されません。
kyo_ready = false
kyo_ready && attack("草薙京") # => 何も出力されません。
puts "---"
# || は左辺が真なら右辺を評価しません(OR短絡)。
# 八神庵のHPが残っているので、デフォルト値は使われません。
iori_hp = 250
current_hp = iori_hp || 0 # iori_hp が nil/false でないのでそのまま 250 です。
puts "八神庵のHP: #{current_hp}"
# 不知火舞のHPが nil の場合、デフォルト値0を使います。
mai_hp = nil
current_hp = mai_hp || 0 # nil なので右辺の 0 が使われます。
puts "不知火舞のHP: #{current_hp}"
puts "---"
# and/or は &&/|| より優先度が低いため、代入と組み合わせたとき挙動が変わります。
# 記号形式(優先度が高い): result に代入されてから and が評価されます。
result = true && false
puts "result (記号): #{result}" # => false
# 英語形式(優先度が低い): result = true が先に評価され、result は true になります。
result = true and false
puts "result (英語): #{result}" # => true(= が and より先に評価される)
# not は ! より優先度が低いですが、読みやすさのために使うことがあります。
rock_ready = true
puts !rock_ready # => false
puts (not rock_ready) # => false
実行すると次のように出力されます。
ruby logical_operators.rb テリー・ボガードが攻撃しました。 --- 八神庵のHP: 250 不知火舞のHP: 0 --- result (記号): false result (英語): true false false
演算子はメソッドとして定義されているため、クラスでオーバーロードできます。KOFキャラクタークラスで演算子を再定義します。
operator_overload.rb
# KOFキャラクターを表すクラスです。
# 演算子をオーバーロードして独自の動作を定義します。
class KofCharacter
include Comparable
attr_reader :name, :power
def initialize(name, power)
@name = name
@power = power
end
# + 演算子: 2人の合体技の威力を表す新しいキャラを返します。
def +(other)
KofCharacter.new("#{@name}&#{other.name}", @power + other.power)
end
# <=> 演算子: 戦闘力で大小を比較します(Comparable に必要)。
# これを定義するだけで <, <=, >, >=, between?, clamp が使えます。
def <=>(other)
@power <=> other.power
end
# == 演算子: 名前と戦闘力が両方等しければ同一とみなします。
def ==(other)
other.is_a?(KofCharacter) && @name == other.name && @power == other.power
end
def to_s
"#{@name}(戦闘力: #{@power})"
end
end
terry = KofCharacter.new("テリー・ボガード", 9500)
kyo = KofCharacter.new("草薙京", 9800)
iori = KofCharacter.new("八神庵", 9600)
mai = KofCharacter.new("不知火舞", 8900)
rock = KofCharacter.new("ロック・ハワード", 9200)
# + 演算子で合体技を作ります。
combined = terry + rock
puts combined # => テリー・ボガード&ロック・ハワード(戦闘力: 18700)
# <=> を定義したので比較演算子がすべて使えます。
puts kyo > terry # => true(草薙京の方が強い)
puts mai < iori # => true(不知火舞の方が弱い)
puts terry >= rock # => true(テリーはロック以上)
# sort で戦闘力順に並べ替えられます。
fighters = [terry, kyo, iori, mai, rock]
fighters.sort.reverse.each.with_index(1) do |c, rank|
puts "#{rank}位: #{c}"
end
実行すると次のように出力されます。
ruby operator_overload.rb テリー・ボガード&ロック・ハワード(戦闘力: 18700) true true true 1位: 草薙京(戦闘力: 9800) 2位: 八神庵(戦闘力: 9600) 3位: テリー・ボガード(戦闘力: 9500) 4位: ロック・ハワード(戦闘力: 9200) 5位: 不知火舞(戦闘力: 8900)
概要
『Ruby』の算術演算子(『+』『-』『*』『/』『%』『**』)は、数値だけでなく文字列や配列にも使えます。整数同士の『/』は小数点以下を切り捨てた商を返す点に注意が必要です。浮動小数点の結果が必要な場合は、どちらか一方を『Float』にする必要があります(例: 『9500.0 / 3』)。
比較演算子では『==』『eql?』『equal?』の使い分けが重要です。『==』は値の等価性、『eql?』は型と値の両方の等価性、『equal?』はオブジェクトの同一性(同じメモリアドレス)を比較します。ハッシュのキー比較では内部的に『eql?』と『hash』メソッドが使われます。
論理演算子の記号形式(『&&』『||』『!』)と英語形式(『and』『or』『not』)は優先度が異なります。英語形式は優先度が非常に低いため、代入式と組み合わせると意図しない結果になることがあります。条件式には記号形式を、制御フローには英語形式を使うのがRubyの慣例です。
Rubyの演算子の多くは通常のメソッドとして実装されているため、クラス内で再定義(オーバーロード)できます。『<=>』を1つ定義して『Comparable』モジュールをインクルードするだけで、6つの比較演算子が自動的に使えるようになります。詳細は『Comparable / <=>』を参照してください。
よくあるミス
ミス1: 整数除算で小数点が消える(7/2 == 3)
整数同士で『/』を使うと、結果も整数に切り捨てられます。小数点以下が必要な場合は、どちらか一方を浮動小数点数にする必要があります。
ng_integer_division.rb
terry_power = 9500 iori_power = 3 # 整数同士の除算は小数点以下が切り捨てられる。 ratio = terry_power / iori_power puts ratio # 3166(3166.666... ではなく整数になる)
実行すると次のように出力されます。
ruby ng_integer_division.rb 3166
正しい書き方:どちらか一方を浮動小数点数(『Float』)にすると、小数点以下が保持されます。
ok_float_division.rb
terry_power = 9500 iori_power = 3 # 一方を Float にすることで小数点以下が得られる。 ratio = terry_power.to_f / iori_power puts ratio # 3166.6666666666665 # または数値リテラルに .0 を付ける。 ratio2 = 9500.0 / 3 puts ratio2 # 3166.6666666666665
実行すると次のように出力されます。
ruby ok_float_division.rb 3166.6666666666665 3166.6666666666665
ミス2: result = value and fallback の優先度トラップ(= より and が低い)
英語形式の『and』は代入演算子『=』より優先度が低いため、『result = value and fallback』は『(result = value) and fallback』と解釈されます。『||』の代わりに使うと意図しない結果になります。
ng_and_precedence.rb
iori_hp = nil # and は = より優先度が低いため、result には nil が代入される。 result = iori_hp and 0 puts result.inspect # nil(0 ではない)
実行すると次のように出力されます。
ruby ng_and_precedence.rb nil
正しい書き方:デフォルト値を設定したい場合は『||』を使います。
ok_or_operator.rb
iori_hp = nil # || はデフォルト値の設定に適している。 result = iori_hp || 0 puts result.inspect # 0 iori_hp = 350 result = iori_hp || 0 puts result.inspect # 350
実行すると次のように出力されます。
ruby ok_or_operator.rb 0 350
ミス3: == と eql? の違い(型まで比較する eql?)
『==』は値の等価性を比較しますが、整数と浮動小数点数のような型違いでも等しいと判定されることがあります。ハッシュのキーに整数と浮動小数点数が混在している場合など、型も含めた比較が必要な場面では『eql?』を使います。
ng_eq_vs_eql.rb
# == は型が違っても値が等しければ true を返す。
puts 9500 == 9500.0 # true
# ハッシュのキーは eql? で比較されるため、整数と浮動小数点数は別キーになる。
powers = { 9500 => "テリー・ボガード" }
puts powers[9500] # テリー・ボガード
puts powers[9500.0] # nil(キーが違う)
実行すると次のように出力されます。
ruby ng_eq_vs_eql.rb true テリー・ボガード nil
正しい書き方:型が重要な場面では『eql?』で意図を明示します。ハッシュキーを統一する場合は型を揃えます。
ok_eql_usage.rb
puts 9500.eql?(9500) # true(型も値も同じ)
puts 9500.eql?(9500.0) # false(型が違う)
# ハッシュキーは型を統一する。
powers = { 9500 => "テリー・ボガード", 9800 => "草薙京", 9600 => "八神庵" }
key = 9500 # 整数で統一
puts powers[key] # テリー・ボガード
実行すると次のように出力されます。
ruby ok_eql_usage.rb true false テリー・ボガード
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。