Нужна помощь в выяснении ошибок компилятора scala - PullRequest
6 голосов
/ 20 мая 2010

Я работал над проектом в 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)))

выдает эту ошибку:
" несоответствие типов;
найдено: (Точка, Точка) => Инт
требуется: (любой, точка) => любой
"

Я очень смущен обоими этими сообщениями об ошибках. Если бы кто-то мог объяснить более четко, что я делаю неправильно, я был бы очень признателен. Да, я вижу, что вторая ошибка говорит о том, что мне нужен тип «Любой», но я не совсем понимаю, как реализовать изменение, которое будет работать так, как мне нужно. Очевидно, что простое изменение «a: Point» на «a: Any» не является жизнеспособным решением, так чего мне не хватает?

Ответы [ 4 ]

6 голосов
/ 20 мая 2010

Тип 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, как говорится в сообщении об ошибке.

2 голосов
/ 20 мая 2010

Другая альтернатива - poly.points.foldLeft(Int.MaxValue)((b, a) => Math.min(b, a.x)), которая также должна работать с Scala 2.7.x. Различия по сравнению с версией reduLeft составляют

  • у вас есть начальное значение (Int.MaxValue в нашем случае, любые реальные данные будут меньше или равны этому)
  • нет ограничений между типом элементов и типом результата, как, например, ограничение нижней границы для reduLeft Тем не менее, решение Eastsun более элегантно.

Кстати, если у вас уже есть классы дел, вы можете опустить ключевое слово new и использовать автоматически сгенерированный фабричный метод в объекте-компаньоне. Таким образом, строка, создающая поли, становится val poly = Polygon(Point(2,5), Point(7,0), Point(3,1)), что немного проще для чтения.

2 голосов
/ 20 мая 2010

Это 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.

1 голос
/ 20 мая 2010

Я вижу, что все, кажется, зацепились за второй фрагмент, поэтому я отвечу на первый:

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 после подчеркивания. Если подчеркивание появляется в каком-либо выражении, таком как вызов метода в вашем примере, тогда это подчеркивание является заполнителем для параметра в анонимной функции.

В этом втором значении особенно важно понимать границы анонимной функции. В частности, анонимная функция будет разделяться самой внутренней скобкой или фигурными скобками, которые ее заключают, или любой запятой.

Теперь применение этого правила к выражению в первом фрагменте означает, что компилятор видит snipper следующим образом:

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: ?}
...