Scala: Pattern matching when one of two items meets some condition

浪子不回头ぞ 提交于 2019-12-04 02:04:10
Thomas Jung

If you have to support arbitrary predicates you can derive from this (which is based on Daniel's idea):

List(v1, v2) filter (_ %2 == 0) match {
    case List(value1, value2) => "a"
    case List(value) => "b"
    case _ => "c"
}

the definition of the function:

def filteredMatch[T,R](values : T*)(f : T => Boolean)(p: PartialFunction[List[T], R]) : R = 
    p(List((values filter f) :_* ))

Now you can use it like this:

filteredMatch(v1,v2)(_ %2 == 0){
    case List(value1, value2) => "a"
    case List(value) => "b"
    case _ => "c"
}

I'm not so sure if it's a good idea (i.e. readable). But a neat exercise nonetheless.

It would be nice if you could match on tuples: case (value1, value2) => ... instead of lists.

I think you're asking two slightly different questions.

One question is how to use "or" in switch statements. || doesn't work; | does. And you can't use variables in that case (because in general they might match different types, which renders the type confusing). So:

def matcher[T](a: (T,T)) = {
  a match {
    case (Some(x),Some(y)) => "both"
    case (Some(_),None) | (None,Some(_)) => "either"
    case _ => "none"
  }
}

Another question is how to avoid having to do this over and over, especially if you want to be able to get at the value in the tuple. I've implemented a version here for Option, but you could use an unwrapped tuple and a boolean.

One trick to achieve this is that to prewrap the values before you start matching on it, and then use your own matching constructs that do what you want. For instance,

class DiOption[+T] {
  def trinary = this
}
case class Both[T](first: T, second:T) extends DiOption[T] { }
case class OneOf[T](it: T) extends DiOption[T] { }
case class Neither() extends DiOption[Nothing] { }
implicit def sometuple2dioption[T](t2: (Option[T],Option[T])): DiOption[T] = {
  t2 match {
    case (Some(x),Some(y)) => Both(x,y)
    case (Some(x),None) => OneOf(x)
    case (None,Some(y)) => OneOf(y)
    case _ => Neither()
  }
}

// Example usage
val a = (Some("This"),None)
a trinary match {
  case Both(s,t) => "Both"
  case OneOf(s) => "Just one"
  case _ => "Nothing"
}

How about this:

    Welcome to Scala version 2.8.0.r20327-b20091230020149 (Java HotSpot(TM) Client VM, Java 1.6.0_17).
Type in expressions to have them evaluated.
Type :help for more information.

scala> def m(v1: Any,v2: Any) = (v1,v2) match {
     |     case (Some(x),Some(y)) => "a"
     |     case (Some(_),None) | (None,Some(_)) => "b"
     |     case _ => "c"
     | }
m: (v1: Any,v2: Any)java.lang.String

scala> m(Some(1),Some(2))
res0: java.lang.String = a

scala> m(Some(1),None)
res1: java.lang.String = b

scala> m(None,None)
res2: java.lang.String = c

scala>

You should be able to do it if you define it as a val first:

val MyValThatIsCapitalized = new OneAndOnlyOne(v: Option[Foo] => v.isDefined )
val result = (v1,v2) match {
  case (Some(value1), Some(value2)) => "a"
  case MyValThatIsCapitalized(value) => "b"
  case _ = > "c"
}

As implied by the name, the name of the val containing the extractor object must be capitalized.

Daniel C. Sobral

On Scala 2.8:

val result = List(v1,v2).flatten match {
  case List(value1, value2) => "a"
  case List(value) => "b"
  case _ = > "c"
}

On Scala 2.7, however, you need a type hint to make it work. So, assuming value is Int, for instance, then:

val result = (List(v1,v2).flatten : List[Int]) match {
  case List(value1, value2) => "a"
  case List(value) => "b"
  case _ = > "c"
}

The funny thing about it is that I misread "first" as "list" on Mitch Blevins answer, and that gave me this idea. :-)

Since you already matched against (Some(x), Some(y)), you may match against (None, None) explicitly, and the remaining cases are (Some(x), None) and (None, Some(y)):

def decide [T](v1: Option[T], v2:Option[T]) = (v1, v2) match {
  case (Some (x), Some (y)) => "a"
  case (None, None)         => "c"
  case _                    => "b"
}

val ni : Option [Int] = None 
decide (ni, ni)            // c
decide (Some (4), Some(3)) // a
decide (ni, Some (3))      // b
decide (Some (4), ni)      // b
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!