ChatGPT解决这个技术问题 Extra ChatGPT

Using comparison operators in Scala's pattern matching system

Is it possible to match on a comparison using the pattern matching system in Scala? For example:

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

The second case statement is illegal, but I would like to be able to specify "when a is greater than".

This can also be used to check if a function evaluates to true, e.g. case x if x.size > 2 => ...
The important thing to understand is that the "patterns" to the left of => operator are indeed "patterns". The 10 in the first case expression you have is NOT the integer literal. So, you can't perform operations (like > check or say function application isOdd(_)) on the left.

B
Ben James

You can add a guard, i.e. an if and a boolean expression after the pattern:

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

Edit: Note that this is more than superficially different to putting an if after the =>, because a pattern won't match if the guard is not true.


Ben, good answer, it really illustrates the importance of pattern guard.
s
seh

As a non-answer to the question's spirit, which asked how to incorporate predicates into a match clause, in this case the predicate can be factored out before the match:

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

Now, the documentation for scala.math.Ordering.compare(T, T) promises only that the non-equal outcomes will be greater than or less than zero. Java's Comparable#compareTo(T) is specified similarly to Scala's. It happens to be conventional to use 1 and -1 for the positive and negative values, respectively, as Scala's current implementation does, but one can't make such an assumption without some risk of the implementation changing out from underneath.


I'm not sure if you're suggesting this as a real solution, but I would strongly recommend against anything that relies on an undocumented convention or assumption.
Exactly. That's why I wrote "one can't make such an assumption without some risk", and qualified my answer as a "non-answer". It's interesting to consider why compare() and compareTo() don't specify 0, 1, and -1 as their codomain.
Math.signum(n compare 10) would guarantee -1, 0 or 1.
This morning I confirmed that nearly six years after writing my original answer, even though the implementation in question moved from one type to another, Scala still maintains that noted behavior of returning -1, 0, or 1.
A valid answer, but personally I don't like this. It's too easy to forget what 0,1, and -1 are supposed to mean.
v
vergenzt

A solution that in my opinion is much more readable than adding guards:

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

Notes:

Ordered.compare returns a negative integer if this is less than that, positive if greater, and 0 if equal.

Int.signum compresses the output from compare to -1 for a negative number (less than 10), 1 for positive (greater than 10), or 0 for zero (equal to 10).


S
Sergii Zhuravskyi

While all the above and bellow answers perfectly answer the original question, some additional information can be found in the documentation https://docs.scala-lang.org/tour/pattern-matching.html , they didn't fit in my case but because this stackoverflow answer is the first suggestion in Google I would like to post my answer which is a corner case of the question above.
My question is:

How to use a guard in match expression with an argument of a function?

Which can be paraphrased:

How to use an if statement in match expression with an argument of a function?

The answer is the code example below:

    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 to scala fiddle : https://scalafiddle.io/sf/G37THif/2 as you can see the case xs if n <= 0 => xs statement is able to use n(argument of a function) with the guard(if) statement.

I hope this helps someone like me.


I
Ireina

Scala's pattern matching allows you to define your own extractor. In this case, you can simply define a new extractor:

class GreaterThan(n: Int) {
  def unapply(i: Int) = i > n
}

val GreaterThan10 = GreaterThan(10)

a match {
  case 10 => ???
  case GreaterThan10() => ???
  case _ => ???
}

or just use pattern guards.