我有一个Angle类,我想表现得像一个浮点数,还有其他行为。我创建了一个类来包含一个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)转换为浮点数,但这很不方便,因为我希望这个类的行为像浮点数一样。在这些情况下,有没有办法自动将Angle转换为float?
答案 0 :(得分: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
我不确定从Numeric继承的副作用会有什么,但不幸的是,这似乎是让代码以您希望的方式工作的唯一方法。
答案 1 :(得分:0)
这就是我自己想出来的。 Math是我真正感兴趣的唯一模块,因此我可以为它编写代理:
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
现在使用上面的Angle类定义:
a = Angle.new(0.0)
# => #<Angle:0x00000000e6dc28 @angle=0.0>
Math.cos(a)
# => 1.0