2012-02-19 23 views
10

Kiedy ponownie dopasowujesz wzorzec do listy, możesz użyć pustej listy do sprawdzenia pustej listy. Jednakże, jeśli typ bazowy jest iterable, nadal można sprawdzić, Nil, i będzie przerwa na zbiór pusty, itp ... Zobacz następujący REPL sesji:Jak zapobiegać tego rodzaju błędom - dopasowywanie wzorców i zerowanie

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 

pytanie brzmi - w jaki sposób można zapobiec tego rodzaju emisyjny? Oczywiście, jeśli l jest listą typów, to nie jest to błąd. A jeśli l jest typu Set, to nie będzie się kompilować. Ale co, jeśli mamy klasę, która ma listę, definiujemy funkcję, która dopasowuje wzorzec w ten sposób, a następnie ktoś zmienia klasę, aby zamiast tego generować iterację? Czy ten wzór Nil kontra _ pasuje ogólnie do złego pomysłu?

+5

Podtyp jest mieczem obosiecznym; używaj ostrożnie. –

Odpowiedz

8

Konwersja scrutinee do listy, aby wyeliminować wątpliwości.

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

Jaka jest szybkość konwersji, jeśli nie jest to lista? Po prawej? – dyross

+0

Tak. '.toSeq' również wystarcza. '(Wektor(): Seq [Int]) dopasowuje {case Nil => 0}'. – retronym

+0

Ale to zależy wyłącznie od tego, że 'Nil.equals' jest overriden, tak że' Nil' jest równe dowolnemu pustemu seq. Jest to nieudokumentowane, więc nie będę na nim polegał (więc lepiej użyj 'toList' zamiast' toSeq' jak w twojej odpowiedzi, wymaga to mniej magii). –

11

Jedną z możliwości jest użycie straży:

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

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

Innym sposobem, aby to zrobić jest użycie if-else-wyrażenie (działa najlepiej, jeśli masz tylko jeden lub dwa warunki, aby sprawdzić):

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

Jeśli jeszcze inaczej to zrobiłem. Po prostu nie jest tak idiomatyczny. – dyross

+0

Imho najlepszym sposobem na kolekcje pasujące do wzorca – lisak

1

Oto kolejna opcja (pun przeznaczone):

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

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

Jest to przydatne w przypadku, w którym wzór mecz, aby uzyskać head jak w popularnej List() match { case h :: t => ... } ale nie jest to lista, to jest Niepowodzenie Iterable i

Dodałem tę odpowiedź, ponieważ uważam, że to dość powszechne dopasowanie do wzorca w kolekcji, aby uzyskać głowę, w przeciwnym razie można po prostu sprawdzić z xs.isEmpty.

Powiązane problemy