2012-02-19 14 views
10

Quando si esegue il modello di corrispondenza di nuovo Elenchi, è possibile utilizzare Nil per verificare la presenza di un elenco vuoto. Tuttavia, se il tipo di fondo è un Iterable, è ancora possibile verificare la presenza di Nil, e si romperà per insieme vuoto, ecc ... Vedi sessione seguente REPL:Come evitare questo tipo di bug - corrispondenza del modello e Nil

scala> val l: Iterable[Int] = List() 
l: Iterable[Int] = List() 

scala> l match { 
    | case Nil => 1 
    | case _ => 2 
    | } 
res0: Int = 1 

scala> val l: Iterable[Int] = Set() 
l: Iterable[Int] = Set() 

scala> l match { 
    | case Nil => 1 
    | case _ => 2 
    | } 
res2: Int = 2 

domanda è - come posso impedire questo tipo di problema? Ovviamente, se l è un elenco di tipi, non è un bug. E se l è di tipo Set, non verrà compilato. Ma se avessimo una classe che ha una lista, definiamo una funzione che il modello corrisponde in questo modo, e poi qualcuno cambia la classe per assumere invece un iter generico? Questo modello Nil vs. _ è in linea con una cattiva idea in generale?

+5

Subtyping è un'arma a doppio taglio; usare con cura. –

risposta

8

Convertire le verifiche in un elenco per eliminare dubbi.

l.toList match { 
    case Nil => 1 
    case xs => 2 
} 
+1

Quanto è lenta la conversione se non è una lista? Sulla destra? – dyross

+0

Sì. anche '.toSeq' è sufficiente. '(Vector(): Seq [Int]) corrisponde {case Nil => 0}'. – retronym

+0

Ma questo si basa unicamente sul fatto che 'Nil.equals' è sovrascritto in modo che' Nil' sia uguale a qualsiasi seq vuoto. Questo non è documentato, quindi non mi fare affidamento su di esso (quindi è meglio usare 'toList' su' toSeq' come nella risposta, comporta meno magia). –

11

Una possibilità è quella di utilizzare una guardia:

scala> val xs: Iterable[Int] = Set() 
xs: Iterable[Int] = Set() 

scala> xs match { case xs if xs.isEmpty => 1 case _ => 2 } 
res0: Int = 1 

Un altro modo per farlo è quello di utilizzare un if-else-espressione (funziona meglio se si hanno solo uno o due condizioni da verificare):

scala> if (xs.isEmpty) 1 else 2 
res1: Int = 1 
+0

L'if else è come ho finito per farlo. Sembra non essere così idiomatico. – dyross

+0

Imho il modo migliore per abbinare le collezioni di pattern – lisak

1

Ecco un'altra opzione (gioco di parole):

scala> val l: Iterable[Int] = List() 
l: Iterable[Int] = List() 

scala> l.headOption match { case None => 1; case Some(h) => 2 } 
res0: Int = 1 

Questo è utile nei casi in cui si pattern match, al fine di ottenere il head come nel popolare List() match { case h :: t => ... } ma non è un elenco, è un Iterable e :: fallirà.

Ho aggiunto questa risposta perché ho pensato che fosse abbastanza comune abbinare una partita a una collezione per ottenere una testa, altrimenti è possibile controllare con xs.isEmpty.

Problemi correlati