【Ruby】演算子

「はじめてのRuby」を読んでのメモ。

今回は第9章「演算子


代入演算子

二項演算子と代入を組み合わせた演算子のこと。
+= や *= などを指す。
変数だけでなく、メソッドを経由したオブジェクトの操作にも使うこともできる。

# 同じ意味を持つ

$stdin.lineno += 1
$stdin.lineno = $stdin.lineno + 1

これらの式は「$stdin.lineno」と「$stdin.lineno=」という2つのメソッドを呼び出していることに注意。

論理演算子の応用

論理演算子は3つの特徴がある。

  • 左側の式から順に評価される
  • 論理式の真偽が決定すると残りの式は評価されない
  • 最後に評価された式の値が論理式全体の値となる



まず、|| について。

条件1 || 条件2

この論理式では必ず条件1、条件2の順に真偽が判定される。ここで条件1の結果が真の時、全体が真となるのは明らかなので、無駄な条件(条件2)の判定は行わないようになっている。逆に、条件1が偽でなければ条件2は評価されない。

条件1 || 条件2 || 条件3

このような場合でも同様である。条件1と条件2の両方が偽にならなければ、条件3の判定は行われない。

var || "Ruby"

この式では、変数 var の真偽が判断され、nil か false の場合にのみ文字列 "Ruby" の真偽が判断される。論理式の戻り値は最後に評価された式の戻り値に一致するので、この式全体の戻り値は、

  • var がオブジェクトを参照していた場合、その値
  • var が nil または false の場合、"Ruby"

となる。


次は && について。
基本的なルールは || と同じである。

条件1 && 条件2

|| の時とは逆に、条件1が真の場合にのみ条件2が評価される。


これらの性質を利用した応用する。

name = "Ruby"    # name にデフォルト値を設定する
if var           # var が nil または false でなければ
  name = var     # name に var を代入する
end

この4行を1行にまとめるとこうなる。

name = var || "Ruby"

次は変数に配列の先頭要素を代入する場合。

item = nil         # item に初期値を設定
if ary             # ary が nil または false でなければ
  item = ary[0]    # ary[0] に item を代入
end

ary が nil でないことを確認してから変数 item への代入を行っている。
これも1行にまとめることができる。

item = nil && ary[0]


# || の代入演算子
var = var || 1
var ||= 1        # 同じ意味

var が nil か false の場合に限り1を代入する、という意味になる。

条件演算子

三項演算子ともいう。条件演算子 ?: は次のように使う。

条件 ? 式1 : 式2

if文に直すとこうなる。

if 条件
  式1
else2
end
a = 1
b = 2
v = (a > b )? a : b
p v    #=> 2


範囲演算子

値の範囲を表すオブジェクト。

# 1から10までを表す範囲オブジェクト
Range.new(1, 10)
1..10    # 省略形

範囲演算子には「..」と「...」の2種類がある。
x ... y の場合、値の範囲は x から y の1つ手前までである。

p (5..10).to_a      #=> [5, 6, 7, 8, 9, 10]
p (5...10).to_a     #=> [5, 6, 7, 8, 9]

p ("a".."e").to_a   #=> ["a", "b", "c", "d", "e"]
p ("a"..."e").to_a  #=> ["a", "b", "c", "d"]

Range オブジェクトの内部では succ メソッドを使っている。

val = "a"
p val  #=> "a"
val = val.succ
p val  #=> "b"
val = val.succ
p val  #=> "c"


演算子の優先順位

演算子には優先順位が設けられている。

優先度 高

::
[]
+(単項演算子) !  ~  
**  
-(単項演算子)
* / %
+ -
<< >>
&
| \
> >= < <=
<=> == === != =~ !~
&&
||
?:(条件演算子)
.. ...
= (+= -= *= /= など含む)
not
and or

優先度 低

優先順位とは違う順番で計算したいときは、() で囲むことでより内側の () の中から順に計算される。

演算子を定義する

Ruby演算子の多くはインスタンスメソッドとして実装されているため、一部を除いてユーザが新たに定義、再定義をして意味を変えることが出来る。

・再定義できない演算子

::   &&   ||   ..   ...   ?:   not   =   and   or


二項演算子

二項演算子を定義するには演算子をメソッド名としてメソッドを定義する。演算子の左の項がレシーバ、右側の項がメソッドの引数として渡される。

class Point
  attr_reader :x, :y

  def initialize(x=0, y=0)
    @x, @y = x, y
  end

  def inspect  # 表示用
    "(#{x}, #{y})"
  end

  def +(other)  # x,yのそれぞれを足す
    self.class.new(x + other.x, y + other.y)
  end

  def -(other)  # x,yのそれぞれを引く
    self.class.new(x - other.x, y - other.y)
  end
end

point0 = Point.new(3, 6)
point1 = Point.new(1, 8)

p point0           #=> (3, 6)
p point1           #=> (1, 8)
p point0 + point1  #=> (4, 14)
p point0 - point1  #=> (2, -2)

二項演算子を定義するときは引数名に「other」がよく用いられる。
なお、self.class.new を Point.new メソッドを使うようにすることもできる。

def +(other) 
  Point.new(x + other.x, y + other.y)
end


単項演算子

定義可能な単項演算子は「+, -, ~, !」の4つ。
それぞれ「+@, -@」といった名前で定義できる。

class Point

  ...

  def +@                     # 自分の複製を返す
    dup
  end

  def -@
    self.class.new(-x, -y)   # x,yのそれぞれの正負を逆にする
  end

  def ~@
    self.class.new(-y, x)    # 90度回転させた座標を返す
  end
end

point = Point.new(3, 6)
p +point  #=> (3, 6)
p -point  #=> (-3, -6)
p ~point  #=> (-6, 3)


添字メソッド

配列やハッシュで用いられる obj[i] と obj[i]=x のことである。それぞれ [ ] と [ ]= という名前で定義できる。

class Point
  attr_accessor :x, :y

  def initialize(x=0, y=0)
    @x, @y = x, y
  end

  def [](index)
    case index
    when 0
      x
    when 1
      y
    else
      raise ArgumentError, "out of range '#{index}'"
    end
  end

  def []=(index,val)
    case index
    when 0
      self.x = val
    when 1
      self.y = val
    else
      raise ArgumentError, "out of range'#{index}'"
    end
  end
end

point = Point.new(3, 6)
p point[0]        #=> 3
p point[1] = 2    #=> 2
p point[1]        #=> 2
p point[2]        #=> Error (ArgumentError)

引数 index が配列でいうところの添字である。今回の場合は、2以上の値をインデックスとして指定した場合に引数に誤りがあることを表す例外を上げている。