Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

背景

  いま、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 に戻させて頂きました。
何卒ご了承くださいますよう。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした