Вопрос о перегрузке оператора ruby
Я занимался рубином и opengl для развлекательных целей, и я решил написать несколько 3D-векторных/плоскостных/и т.д. классов, чтобы выровнять некоторые математические данные.
упрощенный пример:
class Vec3
attr_accessor :x,:y,:z
def *(a)
if a.is_a?(Numeric) #multiply by scalar
return Vec3.new(@x*a, @y*a, @z*a)
elsif a.is_a?(Vec3) #dot product
return @x*a.x + @y*a.y + @z*a.z
end
end
end
v1 = Vec3.new(1,1,1)
v2 = v1*5 #produces [5,5,5]
который все отлично и денди, но я также хочу иметь возможность писать
v2 = 5*v1
который требует добавления функциональности в Fixnum или Float или что-то еще, но я не мог найти способ перегрузить или расширить умножение fixnum, не заменяя его полностью. возможно ли это в рубине? любые советы?
(очевидно, я могу просто написать все мои умножения в правильном порядке, если мне нужно)
Ответы
Ответ 1
Использование coerce - это намного лучший подход, чем обезвреживание базового класса:
class Vec3
attr_accessor :x,:y,:z
def *(a)
if a.is_a?(Numeric) #multiply by scalar
return Vec3.new(@x*a, @y*a, @z*a)
elsif a.is_a?(Vec3) #dot product
return @x*a.x + @y*a.y + @z*a.z
end
end
def coerce(other)
return self, other
end
end
если вы определяете v как v = Vec3.new
, тогда будет работать следующее: v * 5
и 5 * v
Первый элемент, возвращаемый coerce (self), становится новым приемником для операции, а второй элемент (другой) становится параметром, поэтому 5 * v
в точности эквивалентен v * 5
Ответ 2
Я полагаю, что следующее будет делать то, что вы хотите, но предложение banister использовать coerce
вместо обезьян-патчей Numeric
является предпочтительным методом. Используйте этот метод только в случае необходимости (например, если вы хотите, чтобы некоторые двоичные операнды были транзитивными).
Fixnum.class_eval do
original_times = instance_method(:*)
define_method(:*) do |other|
if other.kind_of?(Vec3)
return other * self
else
return original_times.bind(self).call(other)
end
end
end