2012-10-29 5 views
8

seguenti corsi Scala il Coursera, Martin Odersky hanno mostrato un codice di esempio che è:Perché l'intervallo viene trasformato in un vettore dopo l'operazione della mappa?

1 to 5 map (i => i*i) 

E ha detto che il Range si trasforma in una Vector perché condividono la stessa interfaccia (IndexedSeq) e il risultato non poteva essere rappresentato come (era più chiaro nel suo esempio poiché ha generato una coppia che non è rappresentabile come Range).

Non sono sicuro di capire perché penso che abbia detto in precedenza che in un espressione il primo generatore determinerà il tipo di elemento che verrà restituito, e non sembra sempre vero, almeno per Range.

E non sono sicuro di capire perché l'uscita è Vector, perché Vector potrebbe non essere l'unica altra implementazione che può rappresentare il risultato calcolato sopra.

Qualcuno può aiutarmi a capire questa parte per favore?

risposta

9

map prende di nascosto un CanBuildFrom come argomento implicito. Il suo compito è quello di produrre una nuova collezione dato quello che hai già ottenuto (e il tipo di contenuto). Poiché Range non può contenere materiale arbitrario - nemmeno numeri interi arbitrari - non esiste lo CanBuildFrom che produce uno Range.Il supertipo più specifico di Range che ha uno CanBuildFrom è IndexedSeq. La collezione che è effettivamente costruita da questo è un Vector.

+1

Quindi intendete che Scala importa implicitamente per impostazione predefinita alcuni CanBuildFroms impliciti e che alla fine potrei sostituirli o qualcosa del genere? È possibile, solo per capire, produrre qualcosa di diverso da un Vector in questo caso? –

+1

@SebastienLorber - In effetti puoi! Cerca 'breakOut' come forse il modo più conveniente per farlo se alcuni altri impliciti potrebbero già essere applicabili, ad es. http://stackoverflow.com/questions/2592024 –

+0

sì, se fornisci esplicitamente un builder valido puoi ottenere un'altra collezione come risultato – Arjan

1

Vector è l'implementazione predefinita per IndexedSeq. map non può essere rappresentato come Range poiché la classe Range è progettata per contenere una serie di numeri che possono essere rappresentati da un valore di avvio, arresto e gradino (simile a range in Python). I documenti API specificano che si tratta di un caso speciale di IndexSeq.

Possiamo vedere 1 to 5 map { i => i * i } ci porterà un contenitore di valori (1, 4, 9, 16, 25). Possiamo ottenere un inizio e una fermata, ma nessun valore di passo costante.

2

Come sono sicuro che Martin ha anche spiegato, for comprensioni corrispondono a (sono tradotti in) incatenato invocazioni dei metodi map e flatMap (e foreach se non si utilizza yield).

Il motivo per cui generalmente si traduce in un valore del tipo del primo generatore è che map e flatMap generalmente restituiscono lo stesso tipo come loro ricevitore (map sulla List restituisce un List, ecc).

Ora il problema con Range s è che non possono rappresentare cose che non sono sequenze regolari di numeri interi. Di conseguenza, il tipo di reso di map e flatMapas defined for Range non può essere Range. La prossima corrispondenza migliore è Vector, l'implementazione prototipo di una sequenza indicizzata.

(Se si guarda il codice sorgente o anche la pagina di documento Scala a cui mi sono collegato, si vedrà che è un po 'più complicato che solo il tipo di ritorno, ma concettualmente, questo è il motivo Modifica: ... e ora Rex Kerr appena lasciato il CanBuildFrom bomba.)