ما هي جيدة هي الأساليب الارتقادية اليمينية في سكالا؟

StackOverflow https://stackoverflow.com/questions/1162924

سؤال

لقد بدأت للتو اللعب مع Scala، وقد تعلمت للتو حول كيفية إجراء طرق اليمين الارتباط (على عكس أكثر تقليدية الارتقاء الأيسر مشترك في اللغات المنحى للكائنات).

في البداية، عندما رأيت مثالا رمز ل cons قائمة في Scala، لاحظت أن كل مثال كان دائما قائمة على الجانب الأيمن:

println(1 :: List(2, 3, 4))
newList = 42 :: originalList

ومع ذلك، حتى بعد رؤية هذا مرارا وتكرارا، لم أفكر مرتين في ذلك، لأنني لم أكن أعرف (في ذلك الوقت) :: هي طريقة List. وبعد لقد افترضت للتو من مشغل (مرة أخرى، بمعنى المشغل في جاوة) وهذا الزملية لم يهم. حقيقة أن List ظهرت دائما على الجانب الأيمن في المثال، بدا الرمز فقط مقتطف (اعتقدت أنه ربما فقط "الأسلوب المفضل").

الآن أعرف أفضل: يجب أن تكون مكتوبة بهذه الطريقة :: هو اليمين الارتباط.

سؤالي هو، ما هي نقطة القدرة على تحديد الأساليب اليمنى للترابط؟

هل هو بحتة لأسباب جمالية، أو يمكن أن يكون الارتباط الصحيح بالفعل نوع من الفائدة على الارتفاع الأيسر في حالات معينة؟

من وجهة نظر بلدي (المبتدئ)، أنا لا أرى حقا كيف

1 :: myList

هو أفضل من

myList :: 1

ولكن من الواضح أن هذا مثال تافهة أشك في أنه مقارنة عادلة.

هل كانت مفيدة؟

المحلول

الجواب القصير هو أن الارتباط الصحيح يمكن أن يحسن قابلية القراءة من خلال جعل نوع المبرمج يتوافق مع ما يفعله البرنامج فعليا.
لذلك، إذا قمت بكتابة "1 :: 2 :: 3"، يمكنك الحصول على قائمة (1، 2، 3) مرة أخرى، بدلا من الحصول على قائمة مرة أخرى في ترتيب مختلف تماما.
سيكون ذلك بسبب "1 :: 2 :: 3 :: Nilفي الواقع

List[Int].3.prepend(2).prepend(1)

scala> 1 :: 2 :: 3:: Nil
res0: List[Int] = List(1, 2, 3)

أيهما:

  • أكثر قابلية للقراءة
  • أكثر كفاءة (O (1) ل prepend, ، مقابل o (n) من أجل الفرضية append طريقة)

(تذكير، استخراج من الكتاب البرمجة في scala.)
إذا تم استخدام طريقة في تدوين المشغل، مثل a * b, ، يتم استدعاء الطريقة في المعامل الأيسر، كما هو الحال في a.*(b) - ما لم ينتهي اسم الطريقة في القولون.
إذا انتهى اسم الطريقة في القولون، فسيتم استدعاء الأسلوب على المعامل الصحيح.
لذلك، في 1 :: twoThree, ، ال :: يتم استدعاء الأسلوب twoThree, ، تمر في 1، مثل هذا: twoThree.::(1).

بالنسبة لإدراج القائمة، فإنه يلعب دور عملية إلحاق (يبدو أن القائمة قد تم إلحاقها بعد "1" إلى النموذج "1 2 3"، أين في الواقع هو 1 وهذا هو مقدمة إلى القائمة).
لا تقدم قائمة الفئات عملية إلحاق حقيقية، لأن الوقت الذي يستغرقه إلحاق التقدم إلى قائمة ينمو خطيا بحجم القائمة، في حين بإعداد مع :: يأخذ وقت ثابت.
myList :: 1 سيحاول إعدام المحتوى بأكمله في قائمة إعلانات "1"، والتي ستكون أطول من الإضراب 1 لحفظتي (كما في "1 :: myList')

ملاحظة: بغض النظر عن الزائدة التي يكون فيها المشغل، ومع ذلك، يتم تقييم معاملاتها دائما اليسار إلى اليمين.
لذلك إذا كانت B تعبيرا ليس مجرد إشارة بسيطة إلى قيمة ثابتة، فإن A ::: B أكثر تعامل بشكل أدق ككتلة التالية:

{ val x = a; b.:::(x) }

في هذه الكتلة، لا يزال تقييمه قبل ب، ثم يتم تمرير نتيجة هذا التقييم كمعامل إلى B :: طريقة.


لماذا جعل التمييز بين الأساليب النشطة اليسرى والجدية على الإطلاق؟

التي تسمح للحفاظ على ظهور عملية التعاونية المعتادة ('1 :: myList') في الواقع تطبيق العملية على التعبير الصحيح لأنه؛

  • إنه أكثر كفاءة.
  • ولكن هذا هو أكثر قابلية للقراءة مع النظام النقابي معكوس ("1 :: myList' ضد. 'myList.prepend(1)')

لذلك كما تقول، "السكر النحوي"، بقدر ما أعرف.
ملاحظة، في حالة foldLeft, ، على سبيل المثال، قد يكون لديهم ذهب قليلا إلى بعيد (مع ال '/:"معادل المشغل النقابي الأيمن)


لتشمل بعض تعليقاتك، إعادة صياغة قليلا:

إذا كنت تفكر في وظيفة "إلحاق"، والترابط الأيسر، ثم تكتب "oneTwo append 3 append 4 append 5'.
ومع ذلك، إذا كان الأمر يتعلق بوضع 3 و 4 و 5 إلى Onetwo (الذي ستفترض بالمناسبة بالطريقة المكتوبة)، فسيكون ذلك (ن).
نفسه مع "::" إذا كان "إلحاق". لكنها ليست كذلك. هو في الواقع ل "الإعداد"

هذا يعني 'a :: b :: Nil' هو ل 'List[].b.prepend(a)'

إذا "::" كانت لإعدادية، ومع ذلك لا تزال قائمة على اليسار، فستكون القائمة الناتجة في ترتيب خاطئ.
تتوقع أن تقوم بإرجاع القائمة (1، 2، 3، 4، 5)، لكنها سينتهي الأمر إلى قائمة العودة (5، 4، 3، 1، 2)، والتي قد تكون غير متوقعة للمبرمج.
هذا لأنه، ما فعلته سيكون، في النظام النقدي الأيسر:

(1,2).prepend(3).prepend(4).prepend(5) : (5,4,3,1,2)

لذلك، فإن الارتباط الأيمن يجعل التعليمات البرمجية تتطابق مع الترتيب الفعلي لقيمة الإرجاع.

نصائح أخرى

ما هي نقطة القدرة على تحديد الأساليب المنزمية اليمنى؟

أعتقد أن نقطة الأسلوب النقابي الصحيحة هي إعطاء شخص ما فرصة لتوسيع اللغة، وهي نقطة تشغيل متجهة بشكل عام.

Overator Overloading هو شيء مفيد، لذلك قال Scala: لماذا لا تفتحه لأي مزيج من الرموز؟ بدلا من ذلك، لماذا تصنع التمييز بين المشغلين والأساليب؟ الآن، كيف يتفاعل منفذي المكتبة مع أنواع مدمجة مثل Intب في C ++، كانت قد استخدمت friend وظيفة في النطاق العالمي. ماذا لو اريدنا الكل أنواع لتنفيذ المشغل ::?

يعطي الارتباط الأيمن طريقة نظيفة لإضافة :: المشغل إلى جميع الأنواع. بالطبع، من الناحية الفنية :: المشغل هو طريقة نوع القائمة. لكنه أيضا مشغل تصنيع الصنع للمدمجة الدولية و الكل من الأنواع الأخرى، على الأقل إذا كنت تستطيع تجاهل :: Nil في نهايةالمطاف.

أعتقد أنه يعكس فلسفة Scala في تنفيذ أكبر قدر ممكن من الأشياء في المكتبة وجعل اللغة مرنة لدعمها. هذا يسمح بفرصة لشخص ما للتوصل إلى قائمة فائقة، والتي يمكن استدعاؤها باسم:

1 :: 2 :: SuperNil

من المؤسف إلى حد ما أن يكون الارتباط النقابي المناسب مشدود حاليا فقط للقولون في النهاية، لكنني أعتقد أنه يجعل الأمر سهلا بما يكفي لتذكره.

يلعب الارتباط الأيمن والزابير الأيسر دورا مهما عند قيامك بإجراء عمليات طي قائمة. على سبيل المثال:

def concat[T](xs: List[T], ys: List[T]): List[T] = (xs foldRight ys)(_::_)

هذا يعمل بشكل جيد تماما. ولكن لا يمكنك أداء نفس باستخدام عملية foldleft

def concat[T](xs: List[T], ys: List[T]): List[T] = (xs foldLeft ys)(_::_)

،لأن :: هو اليمين الارتباط.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top