вопрос перегрузки оператора ruby - PullRequest
12 голосов
/ 06 декабря 2009

Я возился с 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 без его полной замены. это возможно в рубине? какие-нибудь советы?

(очевидно, я могу просто написать все мои умножения в правильном порядке, если мне нужно)

Ответы [ 2 ]

22 голосов
/ 06 декабря 2009

Использование 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), становится новым получателем для операции, а второй элемент (other) становится параметром, поэтому 5 * v в точности эквивалентно v * 5

0 голосов
/ 06 декабря 2009

Я полагаю, что следующее будет делать то, что вы хотите, хотя предложение перил использовать 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
...