HelloPhysicsWorld->vecmath-ruby->chapter7

 で、overload。こっちは静的と動的言語の差異で、なかなか上手く実装できませんでした。
javaポリモーフィズム多態性)を、rubyでどう実装する*1かって事ですけど。

方法としては、

  1. 型をチェックす
    1. rubyのkind_of?メソッド
    2. 識別メソッドを設定
  2. 関数名の置き換え
    1. C++的なmangle
    2. rubyのalias_method
  3. メソッドの仮引数のデフォルト値

って、辺りでしょうかね。他にも良い方法あれば教えてください^^

型をチェック

 rubyのkind_of?メソッドでクラスをチェックするか、
クラスのシンボルを返すメソッドを定義してチェックするか、です。

class Point2
  def sym; :Point2; end
end
class Point3 < Point2
  def sym; :Point3; end
end
class Point4 < Point3
  def sym; :Point4; end
end

def match_kind_of? obj
  p :Point4 if obj.kind_of? Point4
  p :Point3 if obj.kind_of? Point3
  p :Point2 if obj.kind_of? Point2
end

def match_sym? obj
  p :Point4 if obj.sym == :Point4
  p :Point3 if obj.sym == :Point3
  p :Point2 if obj.sym == :Point2
end

match_kind_of?(Point2.new)   #=>:Point2

match_kind_of?(Point3.new)   #=>:Point3
                             #=>:Point2

match_kind_of?(Point4.new)   #=>:Point4
                             #=>:Point3
                             #=>:Point2


match_sym?(Point2.new)       #=>:Point2

match_sym?(Point3.new)       #=>:Point3

match_sym?(Point4.new)       #=>:Point4

 う〜ん、アルファ版はともかく、リリース版でkind_of?使うのは避けたいですからね。
クラス名のシンボル返すメソッドなんて、普通にありそうだけど、まあ参考で書いてみました−wー

どちらにしても、型をチェックだけ処理量をくう*2ので、避けたい所ですけど><

関数名の置き換え

 C++のmangle的*3に、引数の型情報をset_hogeって感じで追加するか、
alias_methodでクラス特有のアクセサメソッドを定義するか、です。


以下C++のmangle的方法ですが、set使う側が頑張ってくださいってことで><

class Matrix3
  def set(m00,m01,m02,m10,m11,m12,m20,m21,m22)
    #todo
  end
  
  def set_mat3(mat)
    #todo
  end
  
  def set_mat3_mat3(mat1,mat2)
    #todo
  end
  
  def set_vec3_vec3_vec3(vec1,vec2,vec3)
    #todo
  end
end

 次はalias_method使う方法ですが、仮引数としてVector2クラスを想定しているなら、:vx,:vy,:vx=,:vy=を使用して@x,@yを操作する感じです。間違ってPoint2クラスを渡しても、Point2.vx等のアクセサメソッドが定義されてないので、実行時にエラーになります。

まあ実行時ってことで、型安全ではないんですけどw;

module Tuple2
  attr_accessor :x,:y
  
  def initialize x=0,y=0
    @x,@y = x,y
  end

  def add obj1,obj2
    [obj1.x + obj2.x,obj1.y + obj2.y]
  end
end

class Vector2
  include Tuple2
  
  alias_method :vx, :x
  alias_method :vy, :y
  
  alias_method :vx=, :x=
  alias_method :vy=, :y=

  def add_vec vec1,vec2
    [vec1.vx + vec2.vx,vec1.vy + vec2.vy]
  end
end

class Point2
  include Tuple2
  
  alias_method :px, :x
  alias_method :py, :y
  
  alias_method :px=, :x=
  alias_method :py=, :y=

  def add_point point1,point2
    [point1.px + point2.px,point1.py + point2.py]
  end
end

a = Vector2.new
b = Vector2.new
p a.add(a,b)    #=>[0, 0]

x = Point2.new
p a.add(a,p)    #=>C:/freetool/xyzzy/__temp_6idm.rb:9:in `add': undefined method `x' for nil:NilClass (NoMethodError)
                #=>	from C:/freetool/xyzzy/__temp_6idm.rb:46

メソッドの仮引数のデフォルト値

 仮引数に、おかしいデフォルト値を設定する方法です。
func(hoge1),func(hoge1,hoge2),func(hoge1,hoge2,hoge3),...等みたいな感じでポリモーフィズムに対応できるのではと思ってます。

でも結局はチェックコード分処理が、かかる*4んですけどね><

class Vector2
  attr_accessor :x,:y
  def initialize x,y
    @x,@y = x,y
  end
  
  def add vec1=nil,vec2=nil
    unless vec2
      add self,vec1
    else
      @x = vec1.x + vec2.x
      @y = vec1.y + vec2.y
    end
    self
  end
  
  alias_method :+, :add
  
  def to_s
    [@x,@y]
  end
end

a = Vector2.new(1,2)
b = Vector2.new(3,4)
c = Vector2.new(10,10)

p (a + b).to_s         #=>[4, 6]
p (a.add(b,c)).to_s    #=>[13, 14]


 ざっと思いついているのはこんな感じです。
ただ、処理量的なものを気にしないならeval系を使う*5ことでかなり簡潔に書けそうな感じではあるのですけど、私魔法が使えないので><

*1:rubyらしさを求めるなら無意味なことですけどね

*2:vecmathは3Dライブラリですから処理は少しでも削減したい所ですがw;

*3:C++言語のカラクリ 誕生の秘密と舞台裏の第四章の関数オーバーロードを参照

*4:vecmathは3Dライブラリ...

*5:eval系も一概に処理が思いってことはないでしょうからね^^