2010-05-20 12 views
6

Ho lavorato su un progetto in scala, ma ricevo alcuni messaggi di errore che non capisco. Le classi con cui sto lavorando sono relativamente semplici. Per esempio:Ho bisogno di aiuto per capire gli errori dello scalatore

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

Supponiamo ora che creo un poligono:

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

Poi, se si tenta di determinare la posizione e le dimensioni del rettangolo più piccolo possibile che potrebbero contenere il poligono, ottengo vari errori che non capisco.

Di seguito sono riportati frammenti di tentativi diversi e i corrispondenti messaggi di errore che producono.

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

dà l'errore:
"mancante tipo di parametro per la funzione espansa ((x $ 1) => x $ 1.x)"

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

dà questo errore:
"tipo mancata corrispondenza:
trovato: (Punto, Punto) => Int
richiesto: (Qualsiasi, Punto) => Qualsiasi
"

Sono molto confuso su entrambi questi messaggi di errore. Se qualcuno potesse spiegare più chiaramente cosa sto facendo in modo errato, lo apprezzerei molto. Sì, vedo che il secondo errore dice che ho bisogno di digitare "Qualsiasi" ma non capisco esattamente come implementare un cambiamento che funzionerebbe come ho bisogno. Ovviamente cambiare semplicemente "a: Point" in "a: Any" non è una soluzione praticabile, quindi cosa mi manca?

risposta

6

Il tipo di reduceLeft è reduceLeft[B >: A](op: (B, A) => B): B, A è Point, e si stanno cercando di applicarlo a (a: Point, b: Point) => (Math.min(a.x, b.x)).

Il motivi compilatore così:..? Math.min(a.x, b.x) restituisce Int, così Int deve essere un sottotipo di B E B devono anche essere un supertipo di Point Perché B è il tipo di accumulatore, e il suo valore iniziale è il primo Point nel tuo Polygon. Questo è il significato di B >: A.

L'unico supertipo di Int e Point è Any; quindi B è Any e il tipo di op dovrebbe essere (Any, Point) => Any, proprio come dice il messaggio di errore.

+0

Ok ... Penso di averlo capito davvero. Grazie per la spiegazione! – klactose

2

Questo è 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 richiede qui una funzione del tipo (Point, Point) => Point. (Più precisamente (B, Point) => B con B con un limite inferiore per Point. Vedere Scaladoc al metodo di reduceLeft.

+0

Questo restituisce un punto, però nei miei esempi che sto cercando di restituire un Int (le più piccole coordinata x di essere precisi). E credo che il mio problema sia dovuto in qualche modo al fatto che il compilatore si sia confuso su quale tipo debba essere restituito. alterare il codice per restituire un Int (che è quello che ho bisogno): val upperLeftX = poly.points.reduceLeft ((a: punto, b: Point) => if (ax klactose

+0

Prova semplicemente questo: 'val upperLeftX = poly.points.reduceLeft ((a, b) => if (ax

+1

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

2

Un'altra alternativa è poly.points.foldLeft(Int.MaxValue)((b, a) => Math.min(b, a.x)), che dovrebbe funzionare anche con Scala 2.7.x.Le differenze rispetto alla versione reduceLeft sono

  • si dispone di un valore iniziale (Int.MaxValue nel nostro caso, tutti i dati reali saranno minore o uguale a questo)
  • non ci sono vincoli tra il tipo degli elementi e il tipo di risultato, come il vincolo di limite inferiore per riduciLeft Tuttavia la soluzione di Eastsun è più elegante.

BTW se si dispone già di classi di casi, è possibile omettere la nuova parola chiave e utilizzare il metodo di produzione generato automaticamente nell'oggetto associato. Quindi la linea che crea il poly diventa val poly = Polygon(Point(2,5), Point(7,0), Point(3,1)), che è un po 'più facile da leggere.

+0

grazie, terrò a mente le informazioni sulla nuova parola chiave – klactose

+0

@Rahul: grazie per la correzione. Lo stavo cercando a breve, ma non riuscivo a trovarlo :-) –

1

vedo tutti sembrano aver afferrato il secondo frammento di, così risponderò la prima:

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

È previsto che a dire questo:

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

Tuttavia, questo non è come funziona la sottolineatura. Ci sono molti significati da sottolineare, ma due sono rilevanti qui.

In primo luogo, può significare un'applicazione di funzione parziale. Ad esempio, Math.min(_, 0)corrisponderebbe in parte i parametri a min e restituirà una funzione che applica quelli rimanenti. In altre parole, è equivalente a x => Math.min(x, 0), ignorando le annotazioni del tipo. Ad ogni modo, questo significato è solo se il carattere di sottolineatura è tutto da solo al posto di uno (o più) dei parametri.

Questo, tuttavia, non è il caso nel tuo esempio, perché hai aggiunto un dopo il trattino basso. Se il carattere di sottolineatura appare in qualsiasi tipo di espressione, come la chiamata al metodo nell'esempio, allora il segno di sottolineatura è un segnaposto per un parametro in una funzione anonima.

In questo secondo significato è particolarmente importante comprendere i limiti della funzione anonima. In particolare, la funzione anonima sarà delimitata dalla parentesi più interna o parentesi graffe che la racchiude, o da una virgola.

Ora, l'applicazione di questa regola l'espressione nel primo frammento significa che snipper è visto dal compilatore in questo modo:

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

Quindi, ci sono due problemi qui. Innanzitutto, si passano due funzioni a min invece di due doppie. In secondo luogo, poiché min non si aspetta di ricevere funzioni, il compilatore non può inferire quale potrebbe essere il tipo di queste funzioni. Dal momento che non hai fornito alcuna informazione sui tipi di a e di cui sopra, si lamenta di questo.

Se avete fatto fornire tali tipi, il messaggio di errore potrebbe essere qualcosa di simile:

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