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

Ale*_*ack 10 scala pattern-matching extractor

I'm often writing code that compares two objects and produces a value based on whether they are the same, or different, based on how they are different.

So I might write:

val result = (v1,v2) match {
  case (Some(value1), Some(value2)) => "a"
  case (Some(value), None)) => "b"
  case (None, Some(value)) => "b"
  case _ = > "c"
}
Run Code Online (Sandbox Code Playgroud)

Those 2nd and 3rd cases are the same really, so I tried writing:

val result = (v1,v2) match {
  case (Some(value1), Some(value2)) => "a"
  case (Some(value), None)) || (None, Some(value)) => "b"
  case _ = > "c"
}
Run Code Online (Sandbox Code Playgroud)

But no luck.

我在一些地方遇到这个问题,这只是一个具体的例子,更一般的模式是我有两件事,我想知道他们中是否只有一个遇到一些谓词,所以我想写这样的事情:

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

所以这里的想法是OneAndOnlyOne可以配置一个谓词(在这种情况下是isDefined),你可以在多个地方使用它.

以上内容根本不起作用,因为它的向后,谓词需要传递给提取器而不返回.

这样的事怎么样?

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

有:

class OneAndOnlyOne[T](predicate: T => Boolean) {
  def unapply( pair: Pair[T,T] ): Option[T] = {
    val (item1,item2) = pair
    val v1 = predicate(item1)
    val v2 = predicate(item2)

    if ( v1 != v2 )
      Some( if ( v1 ) item1 else item2 )
    else
      None
  }
}
Run Code Online (Sandbox Code Playgroud)

但是,这不编译.

谁能看到让这个解决方案有效的方法?或提出另一种解决方案 我可能比这更复杂:)

Rex*_*err 19

我想你问两个略有不同的问题.

一个问题是如何在switch语句中使用"或".|| 不起作用; | 确实.在这种情况下你不能使用变量(因为通常它们可能匹配不同的类型,这会使类型混乱).所以:

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

另一个问题是如何避免不得不一遍又一遍地执行此操作,尤其是如果您希望能够获得元组中的值.我在这里为Option实现了一个版本,但你可以使用一个解包的元组和一个布尔值.

实现此目的的一个技巧是在开始匹配之前预先打包值,然后使用您自己的匹配结构来执行您想要的操作.例如,

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"
}
Run Code Online (Sandbox Code Playgroud)


Tho*_*ung 7

如果你必须支持任意谓词,你可以从中得出(这是基于Daniel的想法):

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

功能的定义:

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

现在您可以像这样使用它:

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

我不太确定这是不是一个好主意(即可读).但是,尽管如此,这是一次巧妙的运动.

如果你能匹配元组,那就太好了:case (value1, value2) => ...而不是列表.


Eas*_*sun 6

这个怎么样:

    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>
Run Code Online (Sandbox Code Playgroud)