自分で作ったクラスに破壊的メソッドを実装するには、インスタンス変数を直接変更するか、それの応用で replace
メソッドを(Stringクラスみたいに)作ってやればいいのだった。
例えば点を表す Pointクラスでは、こんな感じ。o_point_symmetry!
メソッドは、点を原点対象の点に移す破壊的メソッド。
#点を表すクラス class Point def initialize(x = 0, y = 0) @x = x @y = y end attr_accessor :x, :y def replace(pt) @x = pt.x @y = pt.y self end def o_point_symmetry! self.replace(Point.new(-@x, -@y)) end end pt = Point.new(50, 100) pt.o_point_symmetry! p pt #=> #<Point:*** @x=-50, @y=-100>
もちろんこれは replace
メソッドを作ってみたかっただけで、以下のようにすれば充分。
#点を表すクラス class Point def initialize(x = 0, y = 0) @x = x @y = y end attr_accessor :x, :y def o_point_symmetry! @x = -@x @y = -@y end end pt = Point.new(50, 100) pt.o_point_symmetry! p pt #=> #<Point:*** @x=-50, @y=-100>
逆に言えば、インスタンス変数に代入するとインスタンスメソッドは破壊的になってしまう。なお、o_point_symmetry!
メソッド内の @x, @y
は、レシーバーを表す self
を使った
class Point def o_point_symmetry! self.x = -self.x self.x = -self.y end end
と同じことである。ここいらがよくわかっていなかったところなのだよなあ。
なお、Pointクラスのオブジェクト pt
を、self = pt
のように直接 self
に代入することはできない。上で replace
メソッドを作ったのは、そのためである。このメソッドは、self
への代入のように働く。
それから、破壊的メソッドとは関係ないけれど、クラスにインスタンスメソッド to_s
を作っておくと、puts で出力したときに、自分の好みの形でオブジェクトが出力できる。デバッグに便利らしい。
class Point def to_s "point(#{@x}, #{@y})" end end pt = Point.new(50, 100) puts pt #=> "point(50, 100)"
オブジェクトの変数展開までできる。暗黙に to_s
メソッドが呼ばれるようだ。
class Point def to_s "(#{@x}, #{@y})" end end pt = Point.new(50, 100) puts "点#{pt}" #=> "点(50, 100)"
追記(12/12)
破壊的メソッドと破壊的でないメソッドを作り分ける。
class A def initialize(n) @a = n end attr_reader :a def add!(num) @a += num self end def add(num) A.new(@a + num) end end p x = A.new(1) #=><A:0x007fd426e4d548 @a=1> p x.add!(10) #=><A:0x007fd426e4d548 @a=11> p x #=><A:0x007fd426e4d548 @a=11> p x.add(20) #=><A:0x007fd426e4d250 @a=31> p x #=><A:0x007fd426e4d548 @a=11>