2014-09-20 7 views
5

Wie ist der Kopf und Schwanz in der folgenden Aussage bestimmt:Wie bestimmt Scala Cons Mustervergleich den Kopf und den Schwanz einer Liste?

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

Sollte es nicht irgendein Stück Code sein, der das erste Element als Kopf extrahiert und gibt den Schwanz als eine neue Liste. Ich habe den Code der Scala-Standardbibliothek durchgekämmt und kann nicht verstehen, wie und wo dies geschieht.

+4

http://blog.lunatech.com/2011/11/25/scala-list-extract-demystified gute Erklärung, wie es funktioniert –

+0

@ eugene-zhulenev, danke für den Link. Das war genau richtig. Die Cons-Fallklasse bietet automatisch die Möglichkeit, eine List-Instanz sowohl zu konstruieren als auch zu einem Kopf und einem Schwanz zu dekonstruieren. – poissondist

+0

@poissondist der Link von Eugene Zhulenev ist gebrochen. Was denkst du über meine Antwort unten? – metasim

Antwort

0

Das hier verwendete Scala-Konstrukt ist die Extractor. Das Symbol :: ist nichts anderes als eine Fallklasse in Scala, wo eine unapply-Methode auf ihrem Begleitobjekt existiert, um die Extraktionsmagie zu ermöglichen. Here ist eine gute eingehende Anleitung zu Extraktoren. Aber hier ist die Zusammenfassung:

Immer wenn Sie den Inhalt einer Klasse, entweder für variable Bindung oder als Teil des Mustervergleichs "entpacken" wollen, sucht der Compiler nach der Methode unapply auf welches Symbol auf der linken Seite ist des Ausdrucks. Dies kann ein Objekt, ein Fallklassen-Companion-Objekt (wie ::, in Ihrer Frage) oder eine Instanz mit einem unapply sein. Das Argument zu unapply ist der zu entpackende eingehende Typ, und der Rückgabetyp ist eine Option von dem, was als die erwartete Struktur und die Typen deklariert wurde. Im Mustervergleich zeigt eine None an, dass eine Übereinstimmung nicht gefunden wurde. Bei variabler Bindung wird MatchError ausgelöst, wenn None das Ergebnis ist.

Eine gute Denkweise über unapply ist, dass es das Gegenteil von apply ist. Wobei unapply der Empfänger der Funktionsaufrufsyntax unapply der Empfänger von Extraktoraufrufen ist.

dies weiter zu veranschaulichen, wollen wir einen einfachen Fall Klasse definieren:

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

Weil es ein Fall der Klasse ist, bekommen wir apply und unapply Methoden auf dem Begleitobjekt automatisch erzeugt, die wie folgt aussehen grob:

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)) 
} 

Wenn Sie eine Cat über das Companion-Objekt erstellen, wird apply aufgerufen. Wenn Sie die Bestandteile eines Cat auspacken, wird unapply genannt:

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 

Da unapply kehrt ein Option wir viel Kraft haben Extraktoren zu schreiben, die interessantere Fälle behandeln, zum Beispiel testen, ob ein ankommender Typ entspricht vor dem Extrahieren von Werten einigen Kriterien. Nehmen wir zum Beispiel an, wir möchten den Namen von Katzen bekommen, die "alt" sind.Man könnte dies tun:

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

Verwendung das gleiche wie ein erzeugt unapply wäre:

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 nicht eine schöne Sache zu ermöglichen, also lassen Sie uns Musterabgleich verwenden:

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) 

Das eine zusätzliche bisschen Magie beteiligt mit der unapply Methode für :: ist, dass einige syntaktische Zuckerermöglichtzu schreiben val head :: tail = ... stattdessen.

Verwandte Themen