Scala Pattern matching: Are parametrized extractor objects possible?
Is it possible to create an Extractor object that can be used such as:
val x = 42
x match {
case GreaterThan(80) => println("5")
case GreaterThan(70) => println("4")
case GreaterThan(60) => println("3")
case GreaterThan(40) => println("2")
case _ => println("1")
}
Now I do know that it is possible with if constructs, but I feel that it clutters my code (and seems redundant to do: case MyMatcher(x) if MyCreteria(x) => _
), and I want to avoid that.
Extractor objects that are used in case
statements need to have an unapply
method. Unfortunately and as objects themselves are singletons, so no way (afaik) exists to create such parametrized objects.
In the code below I circumvented this be creating specific singleton object
s for each limit.
@Ákos Vandra: Maybe the following code is helpful (as it comes closest to your requirements):
def main(args: Array[String]) : Unit = {
val n = 42
n match {
case GreaterThan50(x) => println("5")
case GreaterThan40(x) => println("4")
case GreaterThan30(x) => println("3")
case GreaterThan20(x) => println("2")
case _ => println("somewhat")
}
}
class GreaterThanLimit(val limit: Int) {
def unapply(x: Int): Option[Int] = if (x > limit) Some(x) else None
}
object GreaterThan10 extends GreaterThanLimit(10)
object GreaterThan20 extends GreaterThanLimit(20)
object GreaterThan30 extends GreaterThanLimit(30)
object GreaterThan40 extends GreaterThanLimit(40)
object GreaterThan50 extends GreaterThanLimit(50)
Edit 2015-09-17
With the idea from Ákos Vandra's comment, one can use Boolean
instead of Option[_]
and write
def main(args: Array[String]) : Unit = {
val n = 42
n match {
case GreaterThan50() => println("5")
case GreaterThan40() => println("4")
case GreaterThan30() => println("3")
case GreaterThan20() => println("2")
case _ => println("somewhat")
}
}
class GreaterThanLimit(val limit: Int) {
def unapply(x: Int) : Boolean = x > limit
}
object GreaterThan10 extends GreaterThanLimit(10)
object GreaterThan20 extends GreaterThanLimit(20)
object GreaterThan30 extends GreaterThanLimit(30)
object GreaterThan40 extends GreaterThanLimit(40)
object GreaterThan50 extends GreaterThanLimit(50)
It appears that abusing unapply
in the way you want will be very tricky (if possible). Why you don't want to use dedicated if
clause of match
?
object GreaterThan {
def apply(x:Int, boundary:Int) = x > boundary
}
x match {
case x if GreaterThan(x, 80) => println("5")
case x if GreaterThan(x, 70) => println("4")
case _ => println("1")
}
It is not much more verbose than you original code.
Alternatively, you can invent some custom analogue of match:
class Matcher(val cond: Int => Boolean, command: => Unit) {
def evaluate() = command
}
val matchers = Seq(
new Matcher(_ > 80, {println("5")}),
new Matcher(_ > 70, {println("4")}),
new Matcher(_ > 60, {println("3")}),
new Matcher(_ > 40, {println("2")}),
new Matcher(_ => true, {println("2")})
)
val x = 42
matchers.find(_.cond(x)).foreach(_.evaluate())
But it's even more verbose.
Is there any reason you wouldn't just do something like this? It's not especially verbose and saves you a bit of trouble.
x match {
case _ if x > 80 => println("5")
case _ if x > 70 => println("4")
case _ if x > 60 => println("3")
case _ if x > 40 => println("2")
case _ => println("1")
}
链接地址: http://www.djcxy.com/p/29056.html