سؤال

يقال أنه عندما يكون لدينا فصل 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

غير مفهومة:

Point لا يمكن إكراه Fixnum (TypeError)

لذلك نحن بحاجة إلى مزيد من تحديد طريقة مثيل coerce:

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

p 3 * point

انتاج:

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

لذلك يقال ذلك 3 * point بالضبط مثل 3.*(point). أي طريقة المثيل * يأخذ حجة point واستدعى على الكائن 3.

الآن ، منذ هذه الطريقة * لا يعرف كيفية ضرب نقطة ، لذلك

point.coerce(3)

سيتم استدعاؤه ، واستعادة صفيف:

[point, 3]

وثم * هل يتم تطبيقه مرة أخرى على ذلك ، هل هذا صحيح؟

الآن ، هذا مفهوم ولدينا الآن جديد Point كائن ، كما تنفذها طريقة المثيل * التابع Point صف دراسي.

السؤال هو:

  1. الذي يستدعي point.coerce(3)؟ هل هو روبي تلقائيًا ، أم أنه رمز بداخله * طريقة Fixnum عن طريق التقاط استثناء؟ أم أنها بواسطة case بيان أنه عندما لا يعرف أحد الأنواع المعروفة ، ثم اتصل coerce?

  2. يفعل coerce تحتاج دائما إلى إرجاع مجموعة من عنصرين؟ هل يمكن أن يكون أي صفيف؟ أو هل يمكن أن تكون مجموعة من 3 عناصر؟

  3. وهي القاعدة التي ، المشغل الأصلي (أو الطريقة) * هل سيتم استدعاء بعد ذلك على العنصر 0 ، مع حجة العنصر 1؟ (العنصر 0 والعنصر 1 هما العنصرين في تلك الصفيف التي تم إرجاعها بواسطة coerce.) من يفعل ذلك؟ هل يتم ذلك بواسطة Ruby أم يتم ذلك بواسطة Code In 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. ال coerce في ال Point الفصل عام تماما ويعمل مع * أو + لأنها متعدية. ماذا لو لم تكن متعدية ، كما لو أننا نحدد النقطة ناقص FixNum لتكون:

    point = Point.new(100,100)
    point - 20  #=> (80,80)
    20 - point  #=> (-80,-80)
    
هل كانت مفيدة؟

المحلول

إجابة قصيرة: تحقق من كيف Matrix يفعل ذلك.

الفكرة هي ذلك coerce عائدات [equivalent_something, equivalent_self], ، أين equivalent_something هو كائن يعادل أساسا something لكن هذا يعرف كيفية القيام بعمليات على Point صف دراسي. في ال Matrix lib ، نحن نبني أ Matrix::Scalar من أي واحدة Numeric كائن ، وهذا الفئة يعرف كيفية أداء العمليات على Matrix و Vector.

لمعالجة نقاطك:

  1. نعم ، إنه روبي مباشرة (تحقق من المكالمات rb_num_coerce_bin في المصدر) ، على الرغم من أن الأنواع الخاصة بك يجب أن تفعل أيضًا إذا كنت تريد أن يكون رمزك قابلاً للتوسعة من قبل الآخرين. على سبيل المثال إذا Point#* يتم تمرير حجة لا تعترف بها ، ستطلب هذه الحجة coerce نفسها ل Point بالاتصال arg.coerce(self).

  2. نعم ، يجب أن تكون مجموعة من عنصرين ، من هذا القبيل b_equiv, a_equiv = a.coerce(b)

  3. نعم. Ruby يفعل ذلك لأنواع مدمجة ، ويجب عليك أيضًا على أنواعك المخصصة إذا كنت تريد أن تكون قابلاً للتوسعة:

    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 التي تعود مؤقتا بين المستلمة والحجة. لا توجد آلية مصممة تؤمن انتقاد المشغلين +, ==, ، إلخ...

إذا تمكن شخص ما من التوصل إلى وصف دقيق ودقيق وواضح لتحسين الوثائق الرسمية ، اترك تعليقًا!

نصائح أخرى

أجد نفسي في كثير من الأحيان أكتب رمزًا على هذا النمط عند التعامل مع التبادل:

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
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top