Usando operadores de comparação no sistema de correspondência de padrões do Scala

148

É possível comparar em uma comparação usando o sistema de correspondência de padrões no Scala? Por exemplo:

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

A segunda declaração de caso é ilegal, mas eu gostaria de poder especificar "quando a for maior que".

Teorema de Befitting
fonte
1
Isso também pode ser usado para verificar se uma função é avaliada como verdadeira, por exemplo,case x if x.size > 2 => ...
tstenner
2
O importante é entender que os "padrões" à esquerda do operador => são realmente "padrões". O 10 na primeira expressão de caso que você possui NÃO é o literal inteiro. Portanto, você não pode executar operações (como> verificar ou dizer o aplicativo de função isOdd (_)) à esquerda.
Ustaman Sangat

Respostas:

292

Você pode adicionar uma proteção, ou seja, uma ifexpressão booleana e após o padrão:

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

Edit: Observe que isso é mais do que superficialmente diferente de colocar um if após o =>, porque um padrão não será compatível se a guarda não for verdadeira.

Ben James
fonte
3
Ben, boa resposta, isso realmente ilustra a importância da guarda de padrões.
19409 JeffV
32

Como uma não resposta ao espírito da pergunta, que perguntou como incorporar predicados em uma cláusula de correspondência, nesse caso, o predicado pode ser fatorado 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"
    })
}

Agora, a documentação parascala.math.Ordering.compare(T, T) promete apenas que os resultados não iguais serão maiores ou menores que zero . O Java Comparable#compareTo(T)é especificado da mesma forma que o Scala. Por acaso, é convencional usar 1 e -1 para os valores positivo e negativo, respectivamente, como a implementação atual do Scala , mas não se pode fazer essa suposição sem algum risco de a implementação mudar por baixo.

seh
fonte
5
Não tenho certeza se você está sugerindo isso como uma solução real, mas eu recomendaria fortemente contra qualquer coisa que dependa de uma convenção ou suposição não documentada.
Ben James
1
Exatamente. Foi por isso que escrevi "não se pode fazer uma suposição sem risco" e qualifiquei minha resposta como "não resposta". É interessante considerar o motivo compare() e compareTo()não especificar 0, 1 e -1 como seu codomain.
seh
4
Math.signum (n comparar 10) iria garantir -1, 0 ou 1.
richj
1
Esta manhã, confirmei que quase seis anos depois de escrever minha resposta original, embora a implementação em questão tenha mudado de um tipo para outro, Scala ainda mantém esse comportamento notável de retornar -1, 0 ou 1.
seh
2
Uma resposta válida, mas pessoalmente não gosto disso. É muito fácil esquecer o que 0,1 e -1 devem significar.
21416 DanGordon
21

Uma solução que, na minha opinião, é muito mais legível do que adicionar guardas:

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

Notas:

  • Ordered.compareretorna um número inteiro negativo se for menor que isso, positivo se for maior e 0se for igual.
  • Int.signumcomprime a saída de comparepara -1para um número negativo (menor que 10), 1positivo (maior que 10) ou 0zero (igual a 10).
vergenzt
fonte
1

Embora todas as respostas acima e abaixo respondam perfeitamente à pergunta original, algumas informações adicionais podem ser encontradas na documentação https://docs.scala-lang.org/tour/pattern-matching.html , mas não se encaixam no meu caso mas como essa resposta do stackoverflow é a primeira sugestão no Google, eu gostaria de postar minha resposta, que é o caso principal da pergunta acima.
Minha pergunta é:

  • Como usar um guarda na expressão de correspondência com um argumento de uma função?

Que pode ser parafraseado:

  • Como usar uma instrução if na expressão de correspondência com um argumento de uma função?

A resposta é o exemplo de código abaixo:

    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 para o scala fiddle: https://scalafiddle.io/sf/G37THif/2, como você pode ver, a case xs if n <= 0 => xsinstrução é capaz de usar n (argumento de uma função) com a instrução guard (if).

Espero que isso ajude alguém como eu.

Sergii Zhuravskyi
fonte