Uso de operadores de comparación en el sistema de comparación de patrones de Scala

148

¿Es posible hacer coincidir una comparación usando el sistema de coincidencia de patrones en Scala? Por ejemplo:

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

La segunda declaración de caso es ilegal, pero me gustaría poder especificar "cuando a es mayor que".

Teorema apropiado
fuente
1
Esto también se puede usar para verificar si una función se evalúa como verdadera, por ejemplocase x if x.size > 2 => ...
tstenner
2
Lo importante a entender es que los "patrones" a la izquierda del operador => son de hecho "patrones". El 10 en la primera expresión de caso que tiene NO es el literal entero. Por lo tanto, no puede realizar operaciones (como> verificar o decir que la aplicación de función es Add (_)) a la izquierda.
Ustaman Sangat

Respuestas:

292

Puede agregar una protección, es decir, ifuna expresión booleana y después del patrón:

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

Editar: Tenga en cuenta que esto es más que superficialmente diferente a poner un if después del =>, porque un patrón no coincidirá si el guardia no es cierto.

Ben James
fuente
3
Ben, buena respuesta, realmente ilustra la importancia de la protección de patrones.
JeffV
32

Como una no respuesta al espíritu de la pregunta, que preguntaba cómo incorporar predicados en una cláusula de coincidencia, en este caso el predicado se puede factorizar antes de match:

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

Ahora, la documentación descala.math.Ordering.compare(T, T) promesas solo promete que los resultados no iguales serán mayores o menores que cero . Java Comparable#compareTo(T)se especifica de manera similar a Scala. Resulta que es convencional usar 1 y -1 para los valores positivos y negativos, respectivamente, como lo hace la implementación actual de Scala , pero uno no puede hacer tal suposición sin algún riesgo de que la implementación cambie desde abajo.

seh
fuente
55
No estoy seguro de si está sugiriendo esto como una solución real, pero lo recomendaría encarecidamente contra cualquier cosa que se base en una convención o suposición indocumentada.
Ben James
1
Exactamente. Es por eso que escribí "uno no puede hacer tal suposición sin algún riesgo", y califiqué mi respuesta como "no respuesta". Es interesante considerar por qué compare() y compareTo()no especificar 0, 1 y -1 como su codominio.
seh
44
Math.signum (n compare 10) garantizaría -1, 0 o 1.
richj
1
Esta mañana confirmé que casi seis años después de escribir mi respuesta original, a pesar de que la implementación en cuestión se movió de un tipo a otro, Scala aún mantiene ese comportamiento notable de regresar -1, 0 o 1.
seh
2
Una respuesta válida, pero personalmente no me gusta. Es demasiado fácil olvidar lo que significan 0,1 y -1.
DanGordon
21

Una solución que, en mi opinión, es mucho más legible que agregar guardias:

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

Notas:

  • Ordered.comparedevuelve un entero negativo si es menor que eso, positivo si es mayor y 0si es igual.
  • Int.signumcomprime la salida de comparea -1para un número negativo (menor que 10), 1para positivo (mayor que 10) o 0para cero (igual a 10).
vergenzt
fuente
1

Si bien todas las respuestas anteriores y siguientes responden perfectamente a la pregunta original, se puede encontrar información adicional en la documentación https://docs.scala-lang.org/tour/pattern-matching.html , no encajaron en mi caso pero debido a que esta respuesta de stackoverflow es la primera sugerencia en Google, me gustaría publicar mi respuesta, que es un caso de la pregunta anterior.
Mi pregunta es:

  • ¿Cómo usar un protector en la expresión de coincidencia con un argumento de una función?

Que se puede parafrasear:

  • ¿Cómo usar una declaración if en expresión de coincidencia con un argumento de una función?

La respuesta es el siguiente código de ejemplo:

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

enlace al scala fiddle: https://scalafiddle.io/sf/G37THif/2 como puede ver, la case xs if n <= 0 => xsdeclaración puede usar n (argumento de una función) con la declaración de guardia (if).

Espero que esto ayude a alguien como yo.

Sergii Zhuravskyi
fuente