2012-02-19 8 views
10

Cuando el patrón coincide nuevamente Listas, puede usar Nil para verificar si hay una lista vacía. Sin embargo, si el tipo subyacente es un Iterable, todavía se puede comprobar por Nil, y se romperá para los conjuntos vacíos, etc ... Ver siguiente sesión REPL:Cómo evitar este tipo de error - coincidencia de patrones y 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 

La pregunta es - ¿cómo puedo evitar que este tipo de problema? Obviamente, si l es un tipo List, no es un error. Y si l es de tipo Set, no se compilará. Pero, ¿y si tenemos una clase que tiene una lista, definimos una función que coincide con el patrón de esta manera, y luego alguien cambia la clase para tomar un iterable genérico en su lugar? ¿Es este patrón Nil vs. _ una mala idea en general?

+5

Subtipo es una espada de doble filo; usar con cuidado –

Respuesta

8

Convierta el escrutador en una lista para eliminar la duda.

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

¿Qué tan lenta es la conversión si no es una lista? ¿A la derecha? – dyross

+0

Sí. '.toSeq' también es suficiente. '(Vector(): Seq [Int]) match {case Nil => 0}'. – retronym

+0

Pero esto se basa únicamente en el hecho de que 'Nil.equals' se reemplaza para que' Nil' sea igual a cualquier secuencia vacía. Esto no está documentado, así que no confiaría en él (así que mejor usar 'toList' sobre' toSeq' que en tu respuesta, implica menos magia). –

11

Una posibilidad es utilizar un 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 

Otra manera de hacer esto es utilizar un else expresión si-(que funciona mejor si sólo tiene uno o dos condiciones para comprobarlo):

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

La otra cosa es cómo terminé haciéndolo. Parece que no es tan idiomático. – dyross

+0

Imho la mejor manera para colecciones de patrones coincidentes – lisak

1

Aquí es otra opción (juego de palabras):

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

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

Esto es útil en los casos en que el ajuste de patrones con el fin de obtener la head como en el popular List() match { case h :: t => ... } pero no es una lista, que es una Iterable y :: fallarán.

Agregué esta respuesta porque pensé que es bastante común coincidir con el patrón en una colección para obtener una cabeza, de lo contrario, puede consultar con xs.isEmpty.

Cuestiones relacionadas