2011-12-10 11 views
3

私は、浮動小数点のように振る舞いたいAngleクラスを持っています。私は浮動小数点を含むクラスを作成し、それにすべての未知のメソッドを代入します:Rubyで自動的にクラスインスタンスをFloatにキャストするにはどうすればよいですか?

class Angle 
    include Math 
    def initialize(angle=0.0) 
    @angle = Float(angle) 

    # Normalize the angle 
    @angle = @angle.modulo(PI*2) 
    end 

    def to_f 
    @angle.to_f 
    end 

    # Other functionality... 

    def method_missing(message, *args, &block) 
    if block_given? 
     @angle.public_send(message, *args, &block) 
    else 
     @angle.public_send(message, *args) 
    end 
    end 
end 

これは問題なく動作します。しかし、私がtrig操作でそれを使用しようとすると、例えば。 Math.cosは、私が取得:

> a = Angle.new(0.0) 
=> #<Angle:0x00000000cdb220 @angle=0.0> 
@angle=0.0 
> Math.cos(a) 
TypeError: can't convert Angle into Float 

私はfloatに変換するにはフロート(A)を使用することができます知っているが、私は、このクラスは、floatのように動作しますので、それは不便です。このような場合、Angleを自動的に浮動小数点に変換する方法はありますか?

答えて

3

implementation of Math.cosを見ると、Need_Floatというマクロ、which then calls a function rb_to_floatが呼び出されていることがわかります。 Line 2441 of rb_to_float checks to see if the object passed in is of type Numeric。したがって、関数のMathファミリで浮動小数点として独自のクラスを動作させる唯一の方法は、NumericまたはNumericの子孫から継承させることです。このように、あなたのコードのこの変更は、期待どおりに動作:

class Angle < Numeric 
    include Math 
    def initialize(angle=0.0) 
    @angle = Float(angle) 

    # Normalize the angle 
    @angle = @angle.modulo(PI*2) 
    end 

    def to_f 
    @angle.to_f 
    end 

    # Other functionality... 

    def method_missing(message, *args, &block) 
    if block_given? 
     @angle.public_send(message, *args, &block) 
    else 
     @angle.public_send(message, *args) 
    end 
    end 
end 

if __FILE__ == $0 
    a = Angle.new(0.0) 
    p Math.cos(a) 
end 

私は数値から継承どんな副作用がありますわからないんだけど、残念ながらこれはあなたのコードの作業あなたがそれを望むように持っている唯一の方法のように見えますに。

+1

角度は '実装することができませんでしたto_float'? –

+0

私は、to_aryとto_procに類似したto_float強制変換メソッドがあることを期待しましたが、それはルビにはありません。 – phiggy

+0

興味深い回避策。おそらく、FloatメソッドがNumericメソッドの代わりに呼び出されるようにする必要があります。 –

0

これは私が自分自身で思いついたものです。数学は、私は本当に興味が唯一のモジュールであるので、私はそれのためのプロキシを行うことができます。

上から角度クラス定義と今
module Stdlib; end 
::Stdlib::Math = ::Math 
module AngleMath 
    # Copy constants 
    Stdlib::Math.constants.each do |c| 
    self.const_set(c, ::Stdlib::Math.const_get(c)) 
    end 

    def self.map_angles_to_floats(args) 
    args.map do |a| 
     a.kind_of?(Angle)? a.to_f: a 
    end 
    end 

    def self.method_missing(message, *args, &block) 
    if block_given? 
     ::Stdlib::Math.public_send(message, *map_angles_to_floats(args), &block) 
    else 
     ::Stdlib::Math.public_send(message, *map_angles_to_floats(args)) 
    end 
    end 
end 
::Math = AngleMath 

a = Angle.new(0.0) 
# => #<Angle:0x00000000e6dc28 @angle=0.0> 
Math.cos(a) 
# => 1.0 
関連する問題