Domanda

Ho lavorato su un progetto in scala, ma io sono sempre alcuni messaggi di errore che io non capisco. Le classi che sto lavorando con sono relativamente semplici. Ad esempio:

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

Ora supponiamo 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 io non capisco.

Di seguito sono frammenti di diversi tentativi e le 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 non corrispondente;
trovato: (punto, punto) => Int
richiesta: (Qualsiasi, Point) => Qualsiasi
"

Sono molto confuso su entrambi questi messaggi di errore. Se qualcuno potrebbe spiegare più chiaramente quello che sto facendo in modo non corretto, io realmente lo apprezzerei. Sì, vedo che il secondo errore dice che ho bisogno del tipo "qualsiasi", ma non capisco esattamente come implementare un cambiamento che avrebbe funzionato come ne ho bisogno. Ovviamente semplicemente cambiando "un: Punto" a "un: Qualsiasi" non è una soluzione praticabile, così che cosa mi manca?

È stato utile?

Soluzione

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

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

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

Altri suggerimenti

Questa è 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.

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 più piccoli o uguale a questo)
  • non esistono vincoli tra il tipo degli elementi e il tipo del risultato, come il vincolo bound per abbassare reduceLeft Tuttavia la soluzione di Eastsun è più elegante.

A proposito, se si dispone già di classi case è possibile ommit la nuova parola chiave, e possibile utilizzare il metodo factory generato automaticamente nell'oggetto compagno. Così la linea creando poli diventa val poly = Polygon(Point(2,5), Point(7,0), Point(3,1)), che è un po 'più facile da leggere.

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, non è così che funziona di sottolineatura. Ci sono molti significati di sottolineatura, ma due di loro sono rilevanti qui.

In primo luogo, può significare un'applicazione funzione parziale. Ad esempio, sarebbe Math.min(_, 0) applicare parzialmente i parametri min, e restituire una funzione che applica i rimanenti. In altre parole, è equivalente a x => Math.min(x, 0), ignorando le annotazioni di tipo. In ogni caso, questo significato solo vale quando la sottolineatura è da sola al posto di uno (o più) dei parametri.

Che, tuttavia, non è il caso nel tuo esempio, perché si è aggiunto un .x dopo la sottolineatura. Se la sottolineatura appare in qualsiasi tipo di espressione, come ad esempio la chiamata al metodo nel tuo esempio, allora che sottolineatura è un segnaposto per un parametro in una funzione anonima.

In questo secondo significato è particolarmente importante capire i confini della funzione anonima. In particolare, la funzione anonima sarà delimitato da parentesi più interna o parentesi graffe che lo racchiude, o da qualsiasi virgola.

Ora, l'applicazione di questa regola l'espressione dei primi mezzi frammento 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. In primo luogo, si passa due funzioni per min invece di due doppie. In secondo luogo, perché min non si aspetta di ricevere le funzioni, il compilatore non può dedurre quale potrebbe essere il tipo di queste funzioni. Dal momento che non ha fornito alcuna informazione sui tipi di a e b sopra, si lamenta di questo.

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

<console>:6: error: type mismatch;
 found   : Int
 required: ?{val x: ?}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top