2010-05-20 18 views
6

He estado trabajando en un proyecto en scala, pero recibo algunos mensajes de error que no entiendo del todo. Las clases con las que estoy trabajando son relativamente simples. Por ejemplo:Necesito ayuda para descifrar los errores del compilador de scala

abstract class Shape 
case class Point(x: Int, y: Int) extends Shape 
case class Polygon(points: Point*) extends Shape 

Ahora supongamos que yo crear un polígono:

val poly = new Polygon(new Point(2,5), new Point(7,0), new Point(3,1)) 

Entonces si intento para determinar la ubicación y el tamaño del más pequeño rectángulo posible que pudiera contener el polígono, consigo varios errores que no entiendo del todo.

A continuación se muestran fragmentos de diferentes intentos y los mensajes de error correspondientes que producen.

val upperLeftX = poly.points.reduceLeft(Math.min(_.x, _.x)) 

da el error:
"faltante tipo de parámetro para la función ampliada ((x $ 1) => x $ 1.x)"

val upperLeftX = 
     poly.points.reduceLeft((a: Point, b: Point) => (Math.min(a.x, b.x))) 

da este error:
"tipo discordancia;
encontrado: (Punto, Punto) => Int
requerido: (Cualquiera, Punto) => Cualquier
"

Estoy muy confundido acerca de estos dos mensajes de error. Si alguien pudiera explicar más claramente lo que estoy haciendo incorrectamente, realmente lo agradecería. Sí, veo que el segundo error dice que necesito escribir "Cualquiera", pero no entiendo exactamente cómo implementar un cambio que funcione como lo necesito. Obviamente, simplemente cambiar "a: Point" por "a: Any" no es una solución viable, entonces ¿qué me estoy perdiendo?

Respuesta

6

El tipo de reduceLeft es reduceLeft[B >: A](op: (B, A) => B): B, A es Point, y que está tratando de aplicarlo a (a: Point, b: Point) => (Math.min(a.x, b.x)).

El razones compilador así:..? Math.min(a.x, b.x) devuelve Int, por lo Int debe ser un subtipo de B Y B también deben ser un supertipo de Point Por qué B es el tipo de acumulador, y su valor inicial es el primero Point en su Polygon. Ese es el significado de B >: A.

El único supertipo de Int y Point es Any; entonces B es Any y el tipo de op debe ser , tal como aparece en el mensaje de error.

+0

Ok ... Creo que realmente lo entendí. ¡Gracias por la explicación! – klactose

2

Esta es 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 requiere aquí una función del tipo (Point, Point) => Point. (Más precisamente (B, Point) => B con B con una cota inferior a Point. Ver Scaladoc en el método reduceLeft.

+0

Esto devuelve un punto, sin embargo, en mis ejemplos que estoy tratando de devolver un Int (los más pequeños coordenada x estar preciso). Y creo que mi problema radica en que el compilador se confunde en cuanto a qué tipo debería devolver. alterar su código para devolver un Int (que es lo que necesito): val = upperLeftX poly.points.reduceLeft ((a: punto, b: Point) => si (ax klactose

+0

Simplemente intente esto: 'val upperLeftX = poly.points.reduceLeft ((a, b) => if (ax

+1

para scala2.8: val upperLeftX = poly.points.map (_. X) .min – Eastsun

2

Otra alternativa es poly.points.foldLeft(Int.MaxValue)((b, a) => Math.min(b, a.x)), que también debería funcionar con Scala 2.7.x.Las diferencias en comparación con la versión reduceLeft son

  • que tiene un valor de inicio (Int.MaxValue en nuestro caso, los datos reales serán menores o iguales a este)
  • no hay restricciones entre el tipo de los elementos y el tipo de resultado, como la restricción de límite inferior para reducedLeft Sin embargo, la solución de Eastsun es más elegante.

BTW si ya tiene clases de casos, puede omitir la nueva palabra clave y puede usar el método de fábrica generado automáticamente en el objeto complementario. Entonces la línea que crea el poli se convierte en val poly = Polygon(Point(2,5), Point(7,0), Point(3,1)), que es un poco más fácil de leer.

+0

gracias, voy a mantener esa información sobre la nueva palabra clave en mente – klactose

+0

@Rahul: gracias por la corrección. Estaba buscando eso en breve, pero no pude encontrarlo :-) –

1

veo todo el mundo parece haber trabado sobre el segundo fragmento, por lo que voy a responder a la primera:

val upperLeftX = poly.points.reduceLeft(Math.min(_.x, _.x)) 

Se pretende que al decir esto:

val upperLeftX = poly.points.reduceLeft((a, b) => Math.min(a.x, b.x)) 

Sin embargo, eso no es cómo el subrayado funciona Hay muchos significados para subrayar, pero dos de ellos son relevantes aquí.

En primer lugar, puede significar una aplicación de función parcial. Por ejemplo, Math.min(_, 0) sería aplique parcialmente los parámetros a min, y devuelva una función que aplique los restantes. En otras palabras, es equivalente a x => Math.min(x, 0), ignorando las anotaciones de tipo. En cualquier caso, se aplica este significado solo si el guión bajo es en sí mismo en lugar de uno (o más) de los parámetros.

Eso, sin embargo, no es el caso en su ejemplo, porque agregó un .x después del guión bajo. Si el guión bajo aparece en cualquier tipo de expresión, como la llamada al método en su ejemplo, ese guión bajo es un marcador de posición para un parámetro en una función anónima.

En este segundo significado, es particularmente importante comprender los límites de la función anónima. Específicamente, la función anónima estará delimitada por el paréntesis más interior o las llaves que lo encierran, o por cualquier coma.

Ahora, la aplicación de esta regla a la expresión en el primer fragmento significa que snipper es visto por el compilador como esto:

val upperLeftX = poly.points.reduceLeft(Math.min(a => a.x, b => b.x)) 

Por lo tanto, hay dos problemas aquí. Primero, está pasando dos funciones al min en lugar de dos dobles. En segundo lugar, dado que min no espera recibir funciones, el compilador no puede inferir cuál podría ser el tipo de estas funciones. Dado que no proporcionó ninguna información sobre los tipos de a y b anteriores, se queja de eso.

Si estuviese contemplada dicha tipos, el mensaje de error sería algo como esto:

<console>:6: error: type mismatch; 
found : Int 
required: ?{val x: ?} 
Cuestiones relacionadas