2014-09-20 10 views
5

Come è la testa e la coda determinato la seguente dichiarazione:In che modo la corrispondenza del pattern di Scala Cons determina la testa e la coda di un elenco?

val head::tail = List(1,2,3,4); 
//head: 1 tail: List(2,3,4) 

non dovrebbe esserci qualche pezzo di codice che estrae il primo elemento come capo e restituisce la coda come un nuovo elenco. Sto sfogliando il codice della libreria standard di Scala e non riesco a trovare/capire come/dove viene fatto.

+4

http://blog.lunatech.com/2011/11/25/scala-list-extractor-demistificato buona spiegazione come funziona –

+0

@ eugene-zhulenev, grazie per il collegamento. Questo era perfetto. La classe case Contro fornisce automaticamente i mezzi per costruire un'istanza List e decostruirla in una testa e una coda. – poissondist

+0

@poissondist il collegamento da eugene zhulenev è rotto. Cosa ne pensi della mia risposta qui sotto? – metasim

risposta

0

Il costrutto Scala coinvolto qui è il Extractor. Il simbolo :: non è altro che una classe di case in Scala, dove esiste un metodo unapply sul suo oggetto companion per rendere la magia di estrazione accadere. Here è un buon tutorial approfondito sugli estrattori. Ma ecco il riassunto:

Ogni volta che si desidera "decomprimere" il contenuto di una classe, sia per il binding variabile o come parte di pattern matching, il compilatore cerca il metodo unapply su qualunque simbolo si trovi sul lato sinistro dell'espressione. Questo può essere un oggetto, un oggetto compagno della classe case (come ::, nella tua domanda) o un'istanza con un unapply. L'argomento su unapply è il tipo in ingresso da decomprimere e il tipo restituito è un Option di ciò che è stato dichiarato come struttura e tipi previsti. Nella corrispondenza del modello a None indica che non è stata trovata una corrispondenza. Nell'associazione variabile viene generato un numero MatchError se il risultato è None.

Un buon modo di pensare su unapply è che è l'inverso di apply. Dove unapply il sintonizzatore della funzione chiamata sintassi, unapply è il destinatario di chiamate extractor.

Per illustrare questo ulteriore, definiamo una semplice classe caso:

case class Cat(name: String, age: Int) 

Perché è una classe case, veniamo generati automaticamente apply e unapply metodi sull'oggetto compagna, che grosso modo simile a questa:

object Cat { 
    // compiler generated... 
    def apply(name: String, age: Int) = new Cat(name, age)  
    def unapply(aCat: Cat): Option[(String, Int)] = Some((aCat.name, aCat.age)) 
} 

Quando si crea un Cat tramite l'oggetto associato, viene chiamato apply. Quando si estrae gli elementi costitutivi di un Cat, unapply si chiama:

val mycat = Cat("freddy", 3) // `apply` called here 
... 
val Cat(name, age) = mycat // `unapply` called here 
... 
val animal: AnyRef = mycat 
val info = animal match { 
    case Cat(name, age) => "My pet " + name // `unapply` called here 
    case _ => "Not my pet" 
} 
// info: String = My pet freddy 

perché unapply restituisce un Option, abbiamo un sacco di potere di scrivere estrattori che gestiscono i casi più interessanti, per esempio, testare se il tipo di ingresso si conforma ad alcuni criteri prima di estrarre i valori. Ad esempio, supponiamo di voler ottenere il nome di gatti "vecchi".Si potrebbe fare questo:

object OldCatName { 
    def unapply(aCat: Cat) = if (aCat.age >= 10) Some(aCat.name) else None 
} 

Uso sarebbe lo stesso di un generato unapply:

val yourcat = Cat("betty", 12) 
... 
val OldCatName(name1) = yourcat 
// name1: String = "betty" 
val OldCatName(name2) = mycat 
// scala.MatchError: Cat(freddy,3) (of class Cat) 

MatchError s non sono una bella cosa da consentire, quindi cerchiamo di usare il pattern matching:

val conditions = Seq(mycat, yourcat) map { 
    case OldCatName(oldie) => s"$oldie is old" 
    case Cat(name, age) => s"At age $age $name is not old" 
} 
// conditions: Seq[String] = List(At age 3 freddy is not old, betty is old) 

L'un po 'di magia in più con il metodo unapply per :: è che un po' di zucchero sintattico consente val ::(head, tail) = ... da scrivere val head :: tail = ....

Problemi correlati