Korzystanie z operatorów porównania w systemie dopasowywania wzorców Scali

148

Czy możliwe jest dopasowanie w porównaniu przy użyciu systemu dopasowywania wzorców w Scali? Na przykład:

a match {
    case 10 => println("ten")
    case _ > 10 => println("greater than ten")
    case _ => println("less than ten")
}

Drugie stwierdzenie przypadku jest niedozwolone, ale chciałbym móc określić „kiedy a jest większe niż”.

Zgodne twierdzenie
źródło
1
Można tego również użyć do sprawdzenia, czy wartość funkcji jest prawdą, np.case x if x.size > 2 => ...
tstenner
2
Ważną rzeczą do zrozumienia jest to, że „wzorce” po lewej stronie operatora => to rzeczywiście „wzorce”. 10 w pierwszym wyrażeniu, które masz, NIE jest literałem liczby całkowitej. Tak więc nie możesz wykonywać operacji (takich jak> sprawdź lub powiedz, że aplikacja funkcja isOdd (_)) po lewej stronie.
Ustaman Sangat

Odpowiedzi:

292

Możesz dodać strażnika, czyli ifwyrażenie boolowskie po wzorcu:

a match {
    case 10 => println("ten")
    case x if x > 10 => println("greater than ten")
    case _ => println("less than ten")
}

Edycja: Należy pamiętać, że jest to więcej niż powierzchownie różni się położyć if po=> , ponieważ wzorzec nie pasuje, jeśli strażnik nie jest prawdą.

Ben James
źródło
3
Ben, dobra odpowiedź, to naprawdę ilustruje znaczenie ochrony wzoru.
JeffV,
32

Jako brak odpowiedzi na ducha pytania, który pytał, jak włączyć predykaty do klauzuli dopasowania, w tym przypadku predykat można rozłożyć przed match:

def assess(n: Int) {
  println(
    n compare 10 match {
      case 0 => "ten"
      case 1 => "greater than ten"
      case -1 => "less than ten"
    })
}

Teraz dokumentacjascala.math.Ordering.compare(T, T) obiecuje tylko, że nierówne wyniki będą większe lub mniejsze od zera . Java Comparable#compareTo(T)jest określana podobnie do Scali. Zdarza się, że używa się odpowiednio 1 i -1 dla wartości dodatnich i ujemnych, tak jak ma to miejsce w obecnej implementacji Scali , ale nie można przyjąć takiego założenia bez ryzyka, że ​​implementacja zmieni się od spodu.

seh
źródło
5
Nie jestem pewien, czy sugerujesz to jako prawdziwe rozwiązanie, ale zdecydowanie odradzałbym wszystko, co opiera się na nieudokumentowanej konwencji lub założeniu.
Ben James,
1
Dokładnie. Dlatego napisałem „bez ryzyka nie można przyjąć takiego założenia”, a swoją odpowiedź zakwalifikowałem jako „brak odpowiedzi”. Warto zastanowić się, dlaczego compare() i compareTo()nie określać 0, 1 i -1 jako ich kodomeny.
seh
4
Math.signum (n porównać 10) gwarantowałoby -1, 0 lub 1.
richj
1
Dziś rano potwierdziłem, że prawie sześć lat po napisaniu mojej oryginalnej odpowiedzi, mimo że implementacja, o której mowa, przeniosła się z jednego typu do drugiego, Scala nadal utrzymuje, że odnotowano zachowanie zwracania -1, 0 lub 1.
seh
2
Prawidłowa odpowiedź, ale osobiście mi się to nie podoba. Zbyt łatwo jest zapomnieć, co mają oznaczać 0,1 i -1.
DanGordon
21

Rozwiązanie, które moim zdaniem jest dużo bardziej czytelne niż dodanie osłon:

(n compare 10).signum match {
    case -1 => "less than ten"
    case  0 => "ten"
    case  1 => "greater than ten"
}

Uwagi:

  • Ordered.comparezwraca ujemną liczbę całkowitą, jeśli jest mniejsza od tego, dodatnią, jeśli jest większa i 0jest równa.
  • Int.signumkompresuje wynik od comparedo -1dla liczby ujemnej (mniejszej niż 10), 1dla wartości dodatniej (większej niż 10) lub 0dla zera (równej 10).
vergenzt
źródło
1

O ile wszystkie powyższe i poniższe odpowiedzi doskonale odpowiadają na pierwotne pytanie, kilka dodatkowych informacji można znaleźć w dokumentacji https://docs.scala-lang.org/tour/pattern-matching.html , jednak nie pasowały one do mojego przypadku ale ponieważ ta odpowiedź stackoverflow jest pierwszą sugestią w Google, chciałbym zamieścić swoją odpowiedź, która jest narożnym przypadkiem powyższego pytania.
Moje pytanie brzmi:

  • Jak używać strażnika w wyrażeniu dopasowującym z argumentem funkcji?

Które można sparafrazować:

  • Jak używać instrukcji if w wyrażeniu dopasowania z argumentem funkcji?

Odpowiedzią jest poniższy przykład kodu:

    def drop[A](l: List[A], n: Int): List[A] = l match {
      case Nil => sys.error("drop on empty list")
      case xs if n <= 0 => xs
      case _ :: xs => drop(xs, n-1)
    }

link do scala fiddle: https://scalafiddle.io/sf/G37THif/2 jak widać, case xs if n <= 0 => xsinstrukcja może używać n (argument funkcji) z instrukcją guard (if).

Mam nadzieję, że to pomoże komuś takiemu jak ja.

Sergii Zhuravskyi
źródło