في روبي ، كيف يعمل Coerce () فعليًا؟
-
04-10-2019 - |
سؤال
يقال أنه عندما يكون لدينا فصل 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
صف دراسي.
السؤال هو:
الذي يستدعي
point.coerce(3)
؟ هل هو روبي تلقائيًا ، أم أنه رمز بداخله*
طريقةFixnum
عن طريق التقاط استثناء؟ أم أنها بواسطةcase
بيان أنه عندما لا يعرف أحد الأنواع المعروفة ، ثم اتصلcoerce
?يفعل
coerce
تحتاج دائما إلى إرجاع مجموعة من عنصرين؟ هل يمكن أن يكون أي صفيف؟ أو هل يمكن أن تكون مجموعة من 3 عناصر؟وهي القاعدة التي ، المشغل الأصلي (أو الطريقة)
*
هل سيتم استدعاء بعد ذلك على العنصر 0 ، مع حجة العنصر 1؟ (العنصر 0 والعنصر 1 هما العنصرين في تلك الصفيف التي تم إرجاعها بواسطةcoerce
.) من يفعل ذلك؟ هل يتم ذلك بواسطة Ruby أم يتم ذلك بواسطة Code InFixnum
؟ إذا تم ذلك عن طريق الكود في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
لذلك من الصعب حقًا إضافة شيء إلى
Fixnum
طريقة مثيلcoerce
؟ لديها بالفعل الكثير من التعليمات البرمجية فيه ولا يمكننا فقط إضافة بضعة أسطر لتعزيزها (ولكن هل نريد ذلك؟)ال
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
.
لمعالجة نقاطك:
نعم ، إنه روبي مباشرة (تحقق من المكالمات
rb_num_coerce_bin
في المصدر) ، على الرغم من أن الأنواع الخاصة بك يجب أن تفعل أيضًا إذا كنت تريد أن يكون رمزك قابلاً للتوسعة من قبل الآخرين. على سبيل المثال إذاPoint#*
يتم تمرير حجة لا تعترف بها ، ستطلب هذه الحجةcoerce
نفسها لPoint
بالاتصالarg.coerce(self)
.نعم ، يجب أن تكون مجموعة من عنصرين ، من هذا القبيل
b_equiv, a_equiv = a.coerce(b)
نعم. Ruby يفعل ذلك لأنواع مدمجة ، ويجب عليك أيضًا على أنواعك المخصصة إذا كنت تريد أن تكون قابلاً للتوسعة:
def *(arg) if (arg is not recognized) self_equiv, arg_equiv = arg.coerce(self) self_equiv * arg_equiv end end
الفكرة هي أنه يجب عليك التعديل
Fixnum#*
. إذا لم يكن يعرف ماذا تفعل ، على سبيل المثال لأن الحجة هي أPoint
, ، ثم سوف يسألك عن طريق الاتصالPoint#coerce
.النقل العابر (أو فعليًا للتنقل) ليس ضروريًا ، لأن المشغل يسمى دائمًا بالترتيب الصحيح. إنها مجرد دعوة إلى
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