Utilisation des opérateurs de comparaison dans le système de correspondance de modèles de Scala

148

Est-il possible de faire une correspondance sur une comparaison en utilisant le système de correspondance de modèles dans Scala? Par exemple:

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

La deuxième déclaration de cas est illégale, mais j'aimerais pouvoir spécifier "quand a est supérieur à".

Convenant au théorème
la source
1
Cela peut également être utilisé pour vérifier si une fonction est évaluée à vrai, par exemplecase x if x.size > 2 => ...
tstenner
2
La chose importante à comprendre est que les "motifs" à gauche de l'opérateur => sont en effet des "motifs". Le 10 dans le premier cas d'expression que vous avez n'est PAS le littéral entier. Ainsi, vous ne pouvez pas effectuer d'opérations (comme> vérifier ou dire l'application de fonction isOdd (_)) sur la gauche.
Ustaman Sangat

Réponses:

292

Vous pouvez ajouter une garde, c'est-à-dire une ifet une expression booléenne après le motif:

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

Edit: Notez que c'est plus que superficiellement différent de mettre un if après le =>, car un motif ne correspondra pas si la garde n'est pas vraie.

Ben James
la source
3
Ben, bonne réponse, cela illustre vraiment l'importance du patron de garde.
JeffV
32

En tant que non-réponse à l'esprit de la question, qui demandait comment incorporer des prédicats dans une clause de correspondance, dans ce cas, le prédicat peut être pris en compte avant match:

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

Maintenant, la documentation pour lesscala.math.Ordering.compare(T, T) promesses seulement que les résultats non égaux seront supérieurs ou inférieurs à zéro . Java Comparable#compareTo(T)est spécifié de la même manière que Scala. Il se trouve qu'il est conventionnel d'utiliser 1 et -1 pour les valeurs positives et négatives, respectivement, comme le fait l'implémentation actuelle de Scala , mais on ne peut pas faire une telle hypothèse sans un risque que l'implémentation change de dessous.

seh
la source
5
Je ne sais pas si vous suggérez cela comme une vraie solution, mais je vous déconseille fortement tout ce qui repose sur une convention ou une hypothèse non documentée.
Ben James
1
Exactement. C'est pourquoi j'ai écrit "on ne peut pas faire une telle hypothèse sans un certain risque", et j'ai qualifié ma réponse de "non-réponse". Il est intéressant de considérer pourquoi compare() et de compareTo()ne pas spécifier 0, 1 et -1 comme codomaine.
seh
4
Math.signum (n compare 10) garantirait -1, 0 ou 1.
richj
1
Ce matin, j'ai confirmé que près de six ans après avoir écrit ma réponse originale, même si l'implémentation en question est passée d'un type à un autre, Scala maintient toujours ce comportement noté de retour de -1, 0 ou 1.
seh
2
Une réponse valable, mais personnellement je n'aime pas ça. Il est trop facile d'oublier ce que 0,1 et -1 sont censés signifier.
DanGordon
21

Une solution qui à mon avis est bien plus lisible que l'ajout de gardes:

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

Remarques:

  • Ordered.comparerenvoie un entier négatif s'il est inférieur à cela, positif s'il est supérieur et 0s'il est égal.
  • Int.signumcompresse la sortie de compareà -1pour un nombre négatif (inférieur à 10), 1pour positif (supérieur à 10) ou 0pour zéro (égal à 10).
vergenzt
la source
1

Bien que toutes les réponses ci-dessus et ci-dessous répondent parfaitement à la question d'origine, des informations supplémentaires peuvent être trouvées dans la documentation https://docs.scala-lang.org/tour/pattern-matching.html , elles ne correspondaient pas à mon cas mais parce que cette réponse stackoverflow est la première suggestion de Google, je voudrais publier ma réponse qui est un cas secondaire de la question ci-dessus.
Ma question est:

  • Comment utiliser une garde dans une expression de correspondance avec un argument d'une fonction?

Ce qui peut être paraphrasé:

  • Comment utiliser une instruction if dans une expression de correspondance avec un argument d'une fonction?

La réponse est l'exemple de code ci-dessous:

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

lien vers scala fiddle: https://scalafiddle.io/sf/G37THif/2 comme vous pouvez le voir, l' case xs if n <= 0 => xsinstruction est capable d'utiliser n (argument d'une fonction) avec l'instruction guard (if).

J'espère que cela aide quelqu'un comme moi.

Sergii Zhuravskyi
la source