LoginSignup
2
1

More than 5 years have passed since last update.

Ruby のcoerceメソッドで出来ること(備忘メモ)

Last updated at Posted at 2017-01-15

背景

  いま、Rubyの復習がてら、プログラミング言語Rubyを読んでいます。その中でcoerceメソッドの使い方として、「なるほど。そういうことしたいときに使えるのか」と思った例が出ていたので、忘れないようメモしておきます。

動作環境

OS: MacOS 10.11.6
Ruby: 2.2.6

基本動作の確認

  まず、coerceの基本動作の確認。以下のように、1 + 2/3を計算するコードを書く。

require 'rational'

x = 1 + Rational(2,3)

puts x    # => 5/3

  上記で、1 + Rational(2,3)が評価される際に、Ratinalのcoerceメソッドが以下のように、(あるいは以下と同等の式によって)呼ばれている。

Rational(2,3).coerce(1) # => [(1/1), (2/3)]

  この内部の動きの説明として、以下、同著 3.8.7.4 算術演算子の強制型変換 から引用

coerceメソッドは、算術演算子から呼び出される。たとえば、Fixnumが定義している+演算子には、Rationalについての知識がないので、右辺がRatinalなら、どうすればそれを加算できるのかがわからない。corceは、この問題を解決する。数値演算子は、右辺の型の知識がなければ、その右辺のcoerceメソッドを呼び出し、引数として左辺を渡す。FixnumとRationalを加算するサンプルに戻ると、Ratinalのcoerceは、2個のRatinal値の配列を返す。そこで、Fixnumが定義する+演算子は。配列内の値の+演算子を呼び出せばよい。

  上記のコード例だと、Rational(2,3).coerce(1)が、Ratinal値2つの配列[(1/1), (2/3)]を返してくれるので、1 + Rational(2,3)は、Rational(1,1) + Rational(2,3)を評価すればよいことになって、Ratinal値同士の加算に帰着する。

応用例

  次に、本題の「なるほど。そういうことしたいときに使えるのか」の例です。
  以下のような、XY座標上の点XYPointクラスを作成します。このクラスには、引数にスカラー値を期待する、*メソッドによるスカラー倍も定義します。

class XYPoint
  attr_reader :x, :y

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

  def to_s
    "(#@x,#@y)"
  end

  def *(a)
    XYPoint.new(a * @x, a * @y)
  end

end

p = XYPoint.new(2,3)

puts p * 10 # => (20,30)

  ここで、XYPointをベクトルと見るとスカラー倍なら、倍する数10を *の左側にして、

10 * p

と書きたい。しかし、上記のコードで最後の行の、point10をひっくり返して、

puts 10 * p

にすると、以下のような「XYPointはFixnumに変換できない」旨のTypeErrorが発生します。

in `*': XYPoint can't be coerced into Fixnum (TypeError)

  そこで、10 * pを評価するときは、(XYPointの*を使う)p * 10 を評価するように、XYPointクラスに以下のcoerceメソッドを追加します。

class XYPoint

  ・・・・(同上)・・・

  def coerce(a)
    [self, a]
  end
end

こうすると、めでたく以下のように、10 * pの結果(20,30)が表示されました。

puts 10 * p # => (20,30)

参考

リファレンスマニュアル
instance method Numeric#coerce

追記

変数名にpを使うと、Kernel#p と紛らわしいので変数名をpointni

という編集リクエスト(上記メッセージ文末のpointnipointに
の誤字かな?)を一部の方から頂き、その方のお顔を立てて差し上げる
意味あいで、ご要望に添って一度はpoint に修正しておりましたが、
Rails ガイドのサンプルコードにも

スクリーンショット 2017-12-03 16.25.01.png

という、変数名を p とする例がございますし、またこの投稿のコード例でも

p p

というような、明らかに紛らわしいコードを書いているわけではないので、 変数名を p に戻させて頂きました。
何卒ご了承くださいますよう。

2
1
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
2
1