بحاجة إلى مساعدة في اكتشاف أخطاء مترجم سكالا
-
03-10-2019 - |
سؤال
لقد كنت أعمل في مشروع في Scala ، لكنني أتلقى بعض رسائل الخطأ التي لا أفهمها تمامًا. الفصول الدراسية التي أعمل معها بسيطة نسبيا. فمثلا:
abstract class Shape
case class Point(x: Int, y: Int) extends Shape
case class Polygon(points: Point*) extends Shape
افترض الآن أنني أقوم بإنشاء مضلع:
val poly = new Polygon(new Point(2,5), new Point(7,0), new Point(3,1))
ثم إذا حاولت تحديد موقع وحجم أصغر مستطيل ممكن يمكن أن يحتوي على المضلع ، فأنا أحصل على أخطاء مختلفة لا أفهمها تمامًا.
فيما يلي مقتطفات من المحاولات المختلفة ورسائل الخطأ المقابلة التي تنتجها.
val upperLeftX = poly.points.reduceLeft(Math.min(_.x, _.x))
يعطي الخطأ:
"نوع المعلمة المفقودة للدالة الموسعة ((x $ 1) => x $ 1.x)"
val upperLeftX =
poly.points.reduceLeft((a: Point, b: Point) => (Math.min(a.x, b.x)))
يعطي هذا الخطأ:
"عدم تطابق نوع؛
وجدت: (نقطة ، نقطة) => int
مطلوب: (أي ، نقطة) => أي"
أنا في حيرة من أمري بشأن كل من رسائل الخطأ هذه. إذا كان بإمكان أي شخص أن يشرح بشكل أكثر وضوحًا ما أقوم به بشكل غير صحيح ، فأنا أقدر ذلك حقًا. نعم ، أرى أن الخطأ الثاني يقول إنني بحاجة إلى نوع "أي" ، لكنني لا أفهم بالضبط كيفية تنفيذ التغيير الذي سيعمل كما أحتاج إليه. من الواضح ببساطة تغيير "A: Point" إلى "A: any" ليس حلًا قابلًا للتطبيق ، فما الذي أفتقده؟
المحلول
نوع من reduceLeft
هو reduceLeft[B >: A](op: (B, A) => B): B
, A
هو Point
, ، وأنت تحاول تطبيقه على (a: Point, b: Point) => (Math.min(a.x, b.x))
.
أسباب المترجم هكذا: Math.min(a.x, b.x)
عائدات Int
, ، لذا Int
يجب أن يكون نوعًا فرعيًا لـ B
. و B
يجب أن يكون أيضًا مجموعة رائعة من Point
. لماذا ا؟ B
هو نوع المجمع ، وقيمته الأولية هي الأولى Point
في الخاص بك Polygon
. هذا هو معنى B >: A
.
الفائق الوحيد من Int
و Point
هو Any
; ؛ لذا B
هو Any
ونوع op
يجب ان يكون (Any, Point) => Any
, ، كما تقول رسالة الخطأ.
نصائح أخرى
هذا هو Scala 2.8.0.rc2
scala> abstract class Shape
defined class Shape
scala> case class Point(x: Int, y: Int) extends Shape
defined class Point
scala> case class Polygon(points: Point*) extends Shape
defined class Polygon
scala> val poly = new Polygon(new Point(2,5), new Point(7,0), new Point(3,1))
poly: Polygon = Polygon(WrappedArray(Point(2,5), Point(7,0), Point(3,1)))
scala> val upperLeftX = poly.points.reduceLeft((a:Point,b:Point) => if (a.x < b.x) a else b)
upperLeftX: Point = Point(2,5)
reduceLeft
يتطلب هنا وظيفة من النوع (Point, Point) => Point
. (أكثر دقة (B, Point) => B
مع B
مع الحد الأدنى ل Point
. نرى Scaladoc في هذه الطريقة reduceLeft
.
بديل آخر هو poly.points.foldLeft(Int.MaxValue)((b, a) => Math.min(b, a.x))
, ، والتي يجب أن تعمل أيضًا مع Scala 2.7.x. الاختلافات مقارنة بإصدار reduceleft
- لديك قيمة بداية (
Int.MaxValue
في حالتنا ، ستكون أي بيانات حقيقية أصغر أو تساوي ذلك) - لا توجد قيود بين نوع العناصر ونوع النتيجة ، مثل القيد المرتبط السفلي لخفض حل Eastsun أكثر أناقة.
راجع للشغل إذا كان لديك بالفعل فئات حالات ، فيمكنك أن تميل إلى الكلمة الرئيسية الجديدة ، ويمكنك استخدام طريقة المصنع التي تم إنشاؤها تلقائيًا في الكائن المصاحب. لذلك يصبح الخط الذي ينشئ بولي val poly = Polygon(Point(2,5), Point(7,0), Point(3,1))
, ، وهو أسهل قليلاً للقراءة.
أرى أن كل شخص يبدو أنه تم إغلاق المقتطف الثاني ، لذلك سأجيب على الأول:
val upperLeftX = poly.points.reduceLeft(Math.min(_.x, _.x))
كنت تنوي أن يعني هذا:
val upperLeftX = poly.points.reduceLeft((a, b) => Math.min(a.x, b.x))
ومع ذلك ، هذا ليس كيف يعمل السطحي. هناك العديد من المعاني للتطبيق ، لكن اثنان منهم وثيق الصلة هنا.
أولاً ، قد يعني تطبيق الوظيفة الجزئية. فمثلا، Math.min(_, 0)
سيكون تنطبق جزئيا المعلمات ل min
, ، وإرجاع وظيفة تطبق تلك المتبقية. بمعنى آخر ، يعادل x => Math.min(x, 0)
, ، تجاهل نوع التعليقات التوضيحية. على أي حال ، هذا المعنى فقط ينطبق إذا كان السفار السفلي هو نفسه في مكان واحد (أو أكثر) من المعلمات.
ومع ذلك ، ليس هذا هو الحال في مثالك ، لأنك أضفت أ .x
بعد السطح. إذا ظهر السطح السفلي في أي نوع من التعبير ، مثل استدعاء الطريقة في مثالك ، فإن هذا السور هو عنصر نائب للمعلمة في دالة مجهولة.
في هذا المعنى الثاني ، من المهم بشكل خاص فهم حدود الوظيفة المجهولة. على وجه التحديد ، سيتم تحديد الوظيفة المجهولة من خلال أقواس الأعمق أو الأقواس المجعد التي تحيط بها ، أو عن طريق فاصلة.
الآن ، فإن تطبيق هذه القاعدة على التعبير في المقتطف الأول يعني أن القنص ينظر إليه من قبل المترجم مثل هذا:
val upperLeftX = poly.points.reduceLeft(Math.min(a => a.x, b => b.x))
لذلك ، هناك مشكلتان هنا. أولاً ، أنت تمر وظيفتين min
بدلا من اثنين من الزوجي. ثانياً ، لأن min
لا يتوقع استلام الوظائف ، لا يمكن للمترجم أن يستنتج نوع هذه الوظائف. نظرًا لأنك لم تقدم أي معلومات حول أنواع a
و b
أعلاه ، يشكو من ذلك.
إذا قدمت مثل هذه الأنواع ، فستكون رسالة الخطأ شيئًا من هذا القبيل:
<console>:6: error: type mismatch;
found : Int
required: ?{val x: ?}