LoginSignup
6
5

More than 5 years have passed since last update.

Ruby: coerce

Posted at

Ruby の coerce に関するメモです。
coerce はあまり意識することは無いと思いますが、備忘録として記します。

coerce

例として、1K の値(1024)を求めるとします。
以下のようなことをすると、例外が発生します。
(メッセージには「... can't be coerced ...」と出ます。)

p 1 * :K     #=> TypeError! (:K can't be coerced into Fixnum)

coerce できるよう、Symbol#coerce を定義してみます。

class Symbol
  def coerce(other)     # Symbol#coerce を定義する
    equiv = {
      K: 2 ** 10,       # 2の10乗         1,024
      M: 2 ** 20,       # 2の20乗     1,048,576
      G: 2 ** 30,       # 2の30乗 1,073,741,824
    }

    if (other.kind_of? Numeric) && (val = equiv[self])
      [other, val]
    else
      raise
    end
  end
end

p 1 * :K      #=> 1024            (計算できるようになる)
p 1 * :M      #=> 1048576         (     〃          )
p 1 * :G      #=> 1073741824      (     〃          )

できるようになりました。


1 * :K

上の形は糖衣構文であり、実際はメソッド呼び出しです。
「1」がレシーバオブジェクト、「*」がメソッド、「:K」が引数です。

1.*(:K)     # 実際は、こんな形

オブジェクト「1」は「*」というメソッドを知っていますが、そのメソッドで、「:K」(Symbol オブジェクト)を処理する方法を知りません。

その時、「1」は、自分を引数にして「:K」の coerce メソッドを呼び出します。

:K.coerce(self)     # self は「1」

coerce メソッドの返り値として期待しているのは、自分が処理できるように変換された自分と相手の値の配列です。

[変換された自分の値, 変換された相手の値]    # ここで自分が「1」、相手が「:K」

上の例では、Symbol#coerce を定義したので、値が返るようになっています。
(上の例では、[1, 1024] を返しています。Symbol#coerce 内では「:K」視点になるので相手(other)と自分(self)の位置が逆です)

自分と相手の変換された値を受け取った「1」は、以降は普通に「*」(積算)の処理を行って値を返すことができます。(結果は 1 * 1024 = 1024)

p 1 * :K   #=> 1024

参考URI

「1」などNumeric オブジェクトの coerce の仕組みが書かれています。

ちなみに、Numeric#coerce は元からありますが、Symbol#coerce は無いようです。

6
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5