2010-05-09 15 views
56

यह कहा जाता है जब हम एक वर्ग Point है और जानता है कि कैसे की तरह निम्नलिखित point * 3 प्रदर्शन करने के लिए है कि:रूबी में, कॉरर्स() वास्तव में कैसे काम करता है?

class Point 
    def initialize(x,y) 
    @x, @y = x, y 
    end 

    def *(c) 
    Point.new(@x * c, @y * c) 
    end 
end 

point = Point.new(1,2) 
p point 
p point * 3 

आउटपुट:

#<Point:0x336094 @x=1, @y=2> 
#<Point:0x335fa4 @x=3, @y=6> 

लेकिन फिर,

3 * point 

नहीं है समझा:

PointFixnum (TypeError)

मजबूर नहीं किया जा सकता तो हम आगे एक उदाहरण विधि coerce परिभाषित करने की जरूरत:

class Point 
    def coerce(something) 
    [self, something] 
    end 
end 

p 3 * point 

आउटपुट:

#<Point:0x3c45a88 @x=3, @y=6> 

तो यह है ने कहा कि 3 * point3.*(point) जैसा ही है। यही है, उदाहरण विधि * एक तर्क point लेता है और ऑब्जेक्ट 3 पर आक्रमण करता है।

अब, के बाद से इस विधि * एक बिंदु गुणा करने के लिए कैसे पता नहीं है, इसलिए

point.coerce(3) 

बुलाया जाएगा, और एक सरणी वापस पाने:

[point, 3] 

और फिर * एक बार है फिर से लागू, क्या यह सच है?

अब, यह समझा जाता है और अब हमारे पास Point ऑब्जेक्ट है, जैसा कि Point कक्षा के उदाहरण विधि * द्वारा किया गया है।

सवाल यह है:

  1. कौन point.coerce(3) invokes? क्या यह स्वचालित रूप से रूबी है, या यह *Fixnum के अपवाद को पकड़कर कुछ कोड है? या यह case कथन है कि जब यह ज्ञात प्रकारों में से एक नहीं जानता है, तो coerce पर कॉल करें?

  2. क्या coerce हमेशा 2 तत्वों की एक सरणी वापस करने की आवश्यकता है? क्या यह कोई सरणी नहीं हो सकती है? या यह 3 तत्वों की सरणी हो सकती है?

  3. और यह नियम है कि मूल ऑपरेटर (या विधि) * तत्व 1 पर तर्क 1 के साथ तत्व 0 पर लागू किया जाएगा? (तत्व 0 और तत्व 1 उस सरणी में दो तत्व हैं coerce द्वारा लौटाए गए हैं।) यह कौन करता है? क्या यह रूबी द्वारा किया जाता है या यह Fixnum में कोड द्वारा किया जाता है? यदि यह Fixnum में कोड द्वारा किया जाता है, तो यह एक "सम्मेलन" है कि हर कोई जबरदस्ती करते समय चलता है?

    तो यह Fixnum की * में कोड कुछ इस तरह कर रही हो सकता है:

    class Fixnum 
        def *(something) 
        if (something.is_a? ...) 
        else if ... # other type/class 
        else if ... # other type/class 
        else 
        # it is not a type/class I know 
         array = something.coerce(self) 
         return array[0].*(array[1]) # or just return array[0] * array[1] 
        end 
        end 
    end 
    
  4. तो यह वास्तव में कड़ी मेहनत Fixnum के उदाहरण विधि coerce में कुछ जोड़ने की है? यह पहले से ही उस में कोड का एक बहुत कुछ है और हम बस इसे बढ़ाने के लिए कुछ लाइनें नहीं जोड़ सकते हैं (लेकिन हम कभी भी करना चाहते हैं?)

  5. coercePoint कक्षा में काफी सामान्य है और यह * साथ काम करता है या + क्योंकि वे संक्रमणीय हैं। क्या होगा अगर यह इस तरह अगर हम परिभाषित प्वाइंट शून्य से Fixnum होने के लिए के रूप में, सकर्मक नहीं है:

    point = Point.new(100,100) 
    point - 20 #=> (80,80) 
    20 - point #=> (-80,-80) 
    
+1

यह एक उत्कृष्ट सवाल है! मुझे बहुत खुशी है कि मैंने इसे पाया है क्योंकि यह मुझे परेशान कर रहा है और अभी तक मुझे नहीं लगता था कि यह सुलभ था! – sandstrom

+0

एक अच्छा सवाल है। इसे डालने के लिए धन्यवाद। यह कई इंजीनियर-भ्रम-घंटे बचाएगा, मुझे यकीन है। – VaidAbhishek

उत्तर

39

लघु जवाब: how Matrix is doing it की जाँच करें।

विचार यह है कि coerce रिटर्न [equivalent_something, equivalent_self], जहां equivalent_something एक वस्तु मूल रूप से something के बराबर है, लेकिन वह अपने Point वर्ग पर कार्रवाई करने के लिए कैसे जानता है। Matrix lib में, हम Numeric ऑब्जेक्ट से Matrix::Scalar बनाते हैं, और वह वर्ग जानता है कि Matrix और Vector पर संचालन कैसे करें।

अपने अंक को संबोधित करने के लिए:

  1. हाँ, यह रूबी सीधे है (rb_num_coerce_bin in the source के लिए कॉल की जाँच), हालांकि अपने खुद के प्रकार भी करना चाहिए अगर आप अपने कोड दूसरों के द्वारा एक्स्टेंसिबल होना चाहता हूँ। उदाहरण के लिए यदि आपका Point#* एक तर्क पारित किया गया है, तो यह पहचान नहीं आता है, तो आप उस तर्क को coerce पर पर कॉल करके Point पर पूछेंगे।

  2. हाँ, यह 2 तत्वों की एक सरणी, ऐसी है कि b_equiv, a_equiv = a.coerce(b)

  3. हाँ हो गया है। रूबी प्रकार builtin के लिए यह होता है, और तुम भी अपने स्वयं के कस्टम प्रकार पर आप एक्स्टेंसिबल होना चाहता हूँ करना होगा जबकि:

    def *(arg) 
        if (arg is not recognized) 
        self_equiv, arg_equiv = arg.coerce(self) 
        self_equiv * arg_equiv 
        end 
    end 
    
  4. विचार यह है कि आप Fixnum#* संशोधित नहीं करना चाहिए। अगर यह नहीं पता कि क्या करना है, उदाहरण के लिए क्योंकि तर्क Point है, तो यह आपको Point#coerce पर कॉल करके पूछेगा।

  5. पारगमन (या वास्तव में कम्यूटिटी) आवश्यक नहीं है, क्योंकि ऑपरेटर को हमेशा सही क्रम में बुलाया जाता है। यह केवल coerce पर कॉल है जो अस्थायी रूप से प्राप्त और तर्क को वापस लाता है। कोई अंतर्निहित तंत्र कि +, == तरह ऑपरेटरों की commutativity सुनिश्चित करता, आदि ...

कोई आधिकारिक दस्तावेज में सुधार करने के लिए एक, संक्षिप्त सटीक और स्पष्ट विवरण के साथ आने कर सकते हैं, एक टिप्पणी छोड़ रहा है!

+0

एचएम, पारगमनशीलता वास्तव में कोई फर्क नहीं पड़ता है? उदाहरण के लिए, http://stackoverflow.com/questions/2801241/in-ruby-how-to-implement-20-point-and-point-20-using-coerce –

+0

नहीं, पारगमन कोई भूमिका निभाता नहीं है और रूबी यह नहीं मानती कि 'ए - बी' '- (बी - ए) 'या उसके जैसा कुछ भी है, यहां तक ​​कि' ए + बी == बी + ए 'भी नहीं है। आपको क्या विश्वास है कि मैं गलत हूं? क्या आपने एमआरआई स्रोत की जांच की? मैं जिस दिशा का संकेत देता हूं उसका पालन करने का प्रयास क्यों नहीं करें? –

+0

मुझे लगता है कि ओपी का अर्थ "संक्रमणीय" के बजाय "सममित" था। किसी भी मामले में ** मैं ** जानना चाहता हूं कि आप 'कॉरर्स' कैसे लिखते हैं जैसे गैर-सममित ऑपरेटर '-' जैसे ही एक दिशा में कार्यान्वित किए जा सकते हैं जबकि सममित ऑपरेटर दोनों तरीकों से काम करते हैं। दूसरे शब्दों में 'ए + 3 == 3 + ए' और '3 + ए -3 == ए' लेकिन' 3 - ए' एक त्रुटि उठाता है। –

2

मुझे लगता है अपने आप को अक्सर जब commutativity के साथ काम कर इस पैटर्न के साथ कोड लिखने:

class Foo 
    def initiate(some_state) 
    #... 
    end 
    def /(n) 
    # code that handles Foo/n 
    end 

    def *(n) 
    # code that handles Foo * n 
    end 

    def coerce(n) 
     [ReverseFoo.new(some_state),n] 
    end 

end 

class ReverseFoo < Foo 
    def /(n) 
    # code that handles n/Foo 
    end 
    # * commutes, and can be inherited from Foo 
end 
संबंधित मुद्दे