2009-12-06 30 views
11

Tôi đã được rối tung xung quanh với ruby ​​và opengl cho các mục đích giải trí, và tôi quyết định viết một số 3d vector/máy bay/etc lớp học để khá lên một số toán học.ruby ​​câu hỏi quá tải điều hành

đơn giản hóa ví dụ:

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] 

mà tất cả tiền phạt và dandy, nhưng tôi cũng muốn để có thể viết

v2 = 5*v1 

mà đòi hỏi phải bổ sung thêm chức năng để Fixnum hoặc nổi hoặc bất cứ điều gì, nhưng tôi couldn không tìm cách để quá tải hoặc mở rộng phép nhân của fixnum mà không thay thế nó hoàn toàn. điều này có thể xảy ra trong ruby ​​không? bất kỳ lời khuyên nào?

(rõ ràng là tôi chỉ có thể viết tất cả các phép nhân của tôi theo đúng thứ tự nếu tôi cần phải)

+0

Chỉ dành cho th e record, thay đổi '@ x * s, @ y * s, @ z * s' thành' @ x * a, @ y * a, @ z * a', nếu không mã của bạn bị hỏng. –

+0

cảm ơn, sao chép mã từ 2 địa điểm cùng một lúc>

Trả lời

21

Sử dụng ép buộc là một cách tiếp cận MUCH tốt hơn so với một lớp lõi khỉ vá:

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 

nếu bạn định nghĩa v như v = Vec3.new thì đây sẽ làm việc: v * 55 * v Yếu tố đầu tiên được trả về bởi coerce (self) trở thành người nhận mới cho hoạt động và phần tử thứ hai (khác) trở thành thông số, vì vậy 5 * v chính xác tương đương với v * 5

+1

+1 để ép buộc. Thay mặt cho người đã gỡ lỗi mã của bạn, xin vui lòng không khỉpatch các lớp lõi trừ khi siêu-duper-hoàn toàn cần thiết. – zenazn

+0

điều này làm việc tuyệt vời cho những gì tôi cần. nếu tôi chạy vào một ví dụ tương tự mà không thể giao hoán thì tôi giả sử tôi sẽ vá khỉ khi cần thiết;) –

+0

Coerce phải luôn trả về 'self' làm đối số thứ hai! Nếu không, bạn sẽ làm rối loạn giao hoán của các đối số. Thay vào đó, 'coerce' sẽ đưa đối số vào một kiểu có thể được nhân lên (ví dụ:' Vec3.new (khác, khác, khác) '). Rõ ràng đây không phải là không có vấn đề riêng của nó. –

-1

Tôi tin rằng sau đây sẽ làm những gì bạn muốn, mặc dù banister's suggestion sử dụng coerce thay vì khỉ vá Numeric là một ưu tiên phương pháp. Chỉ sử dụng phương pháp này nếu cần thiết (ví dụ: nếu bạn chỉ muốn một số toán hạng nhị phân nhị phân để chuyển đổi).

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 
+1

mmm sexy :) btw, tôi cần thay đổi dòng đầu tiên thành "Fixnum.class_eval do" hoặc (tương đương?) "Class Fixnum" –

+1

là nó không thể chỉ để xác định nó trực tiếp trên lớp Fixnum mà không có class_eval, và để làm một def thường xuyên hơn là một define_method? – horseyguy

+0

ok, mã này là điên :) không chỉ làm bạn không cần một class_eval (một cơ thể lớp bình thường sẽ làm tốt) bạn đang làm những điều vô lý với các đối tượng phương pháp không ràng buộc đó là hoàn toàn không cần thiết. Tại sao không chỉ sử dụng alias_method thay thế? ;) Chưa kể rằng khỉ vá một lớp cốt lõi là một không lớn không quá :) – horseyguy

Các vấn đề liên quan