Scala:当两项中的一项满足某些条件时进行模式匹配
我经常编写代码来比较两个对象,并根据它们是相同的还是不同的,以及它们的不同程度来生成一个值 所以我可以写:Scala:当两项中的一项满足某些条件时进行模式匹配,scala,pattern-matching,extractor,Scala,Pattern Matching,Extractor,我经常编写代码来比较两个对象,并根据它们是相同的还是不同的,以及它们的不同程度来生成一个值 所以我可以写: val result = (v1,v2) match { case (Some(value1), Some(value2)) => "a" case (Some(value), None)) => "b" case (None, Some(value)) => "b" case _ = > "c" } 第二个和第三个案例实际上是一样的,所以我试着写
val result = (v1,v2) match {
case (Some(value1), Some(value2)) => "a"
case (Some(value), None)) => "b"
case (None, Some(value)) => "b"
case _ = > "c"
}
第二个和第三个案例实际上是一样的,所以我试着写:
val result = (v1,v2) match {
case (Some(value1), Some(value2)) => "a"
case (Some(value), None)) || (None, Some(value)) => "b"
case _ = > "c"
}
但是没有运气
我在一些地方遇到了这个问题,这只是一个具体的例子,更一般的模式是我有两件事,我想知道它们中是否有一件,而且只有一件符合某个谓词,所以我想这样写:
val result = (v1,v2) match {
case (Some(value1), Some(value2)) => "a"
case OneAndOnlyOne(value, v: Option[Foo] => v.isDefined ) => "b"
case _ = > "c"
}
filteredMatch(v1,v2)(_ %2 == 0){
case List(value1, value2) => "a"
case List(value) => "b"
case _ => "c"
}
因此,这里的想法是,可以使用谓词(在本例中定义)配置OneAndOnlyOne,并且您可以在多个位置使用它
上面的方法根本不起作用,因为它是向后的,谓词需要传递到提取器,而不是返回
像这样的怎么样
val result = (v1,v2) match {
case (Some(value1), Some(value2)) => "a"
case new OneAndOnlyOne(v: Option[Foo] => v.isDefined )(value) => "b"
case _ = > "c"
}
与:
但是,这不是编译
有人能想出一个办法让这个解决方案奏效吗?还是提出另一个解决方案?我可能把事情弄得更复杂了:)这个怎么样:
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>
如果首先将其定义为val,则应该能够执行此操作:
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"
}
正如名称所暗示的那样,包含提取器对象的val的名称必须大写。我想你问的是两个稍微不同的问题 一个问题是如何在switch语句中使用“or”不起作用做在这种情况下,您不能使用变量(因为通常情况下,它们可能匹配不同的类型,这会导致类型混淆)。因此: 另一个问题是如何避免反复执行此操作,特别是如果您希望能够获取元组中的值。我在这里为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"
}
在Scala 2.8上:
val result = List(v1,v2).flatten match {
case List(value1, value2) => "a"
case List(value) => "b"
case _ = > "c"
}
然而,在Scala2.7上,您需要一个类型提示来使其工作。因此,假设value
是Int
,例如,那么:
val result = (List(v1,v2).flatten : List[Int]) match {
case List(value1, value2) => "a"
case List(value) => "b"
case _ = > "c"
}
有趣的是,我把答案上的“第一个”误读为“列表”,这让我产生了这个想法。:-) 如果必须支持任意谓词,则可以从中派生(基于): 函数的定义:
def filteredMatch[T,R](values : T*)(f : T => Boolean)(p: PartialFunction[List[T], R]) : R =
p(List((values filter f) :_* ))
现在您可以这样使用它:
val result = (v1,v2) match {
case (Some(value1), Some(value2)) => "a"
case OneAndOnlyOne(value, v: Option[Foo] => v.isDefined ) => "b"
case _ = > "c"
}
filteredMatch(v1,v2)(_ %2 == 0){
case List(value1, value2) => "a"
case List(value) => "b"
case _ => "c"
}
我不太确定这是否是一个好主意(即可读性)。但这是一个巧妙的练习
如果您可以匹配元组:
case(value1,value2)=>…
而不是列表,那就太好了。因为您已经匹配了(一些(x),一些(y)),您可以显式匹配(无,无),剩下的案例是(一些(x),无)和(无,一些(y)):
正则表达式作为case的用法是一样的。谢谢Mitch,我确实试过了,我想它失败了,因为我的val没有大写。。有意思。你有一个用例来说明你为什么要这么做吗?也许使用
或者用例存在更好的解决方案-我主要是想避免编写这两个用例,例如(一些,没有)和(没有,一些)。我的通用用例是,我正在比较两个产品的一个特定功能,可能两个产品都有该功能,或者只有一个,或者如果它们都有,可能其中一个对该功能有很好的价值,而另一个价值很低。可能重复感谢Daniel的回答,这看起来不错,但只适用于谓词“x=>x.isDefined”,我不能用它来表示“x=>x%2==0”来检查这两项中是否有一项是偶数。@Alex-你可以这样表达:List(v1,v2)filter(%2==0)match{case List(value1,value2)=>“a”case List(value)=>“b”case=>“c”)首先过滤列表。我认为Daniel的解决方案是最直接的。@Alex:我想通过一些工作,你可以提取模式:def xMatch(values:List[T],f:T=>Boolean,p:List[PartialFunction[T,X]])
@Alex:我已经对它进行了一些修补:嗨,Rex,看起来很有希望,但它还没有扩展到其他情况,例如,其中一个不是要求定义其中一个,而是要求其中一个说“是偶数”或“值大于10”。是不是:case(Some(x),Some(y)),如果((x>10)&(y isEven))=>“两者”都起作用?我不确定我是否在跟踪Jim,但是:是的,这确实起作用,但我不认为这是我要说的。我想在概念上这样做:Case(oneisEven)=>Case(bothAreEven)=>。例如,它们是否相等只是一个不同的测试,而不是它们是否都没有。这看起来很简单,我想我在Scala 2.7中试过,但它没有编译,我会再试一次。伙计,我喜欢Scala。它的可读性再好不过了,它是一个函数。还有一艘客轮。
filteredMatch(v1,v2)(_ %2 == 0){
case List(value1, value2) => "a"
case List(value) => "b"
case _ => "c"
}
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