Scala:返回布尔值的模式匹配的简短forms

我发现自己经常写这样的东西:

a match { case `b` => // do stuff case _ => // do nothing } 

有没有更简单的方法来检查一些值是否符合模式? 我的意思是,在这种情况下,我可以只写if (a == b) // do stuff ,但如果模式更复杂呢? 就像匹配列表或任意复杂的模式一样。 我想能够写这样的事情:

 if (a matches b) // do stuff 

我相对比较新的斯卡拉,所以请原谅,如果我想念的东西大:)

这正是我写这些function的原因,因为没有人提到这些function,这些function显然令人印象深刻。

 scala> import PartialFunction._ import PartialFunction._ scala> cond("abc") { case "def" => true } res0: Boolean = false scala> condOpt("abc") { case x if x.length == 3 => x + x } res1: Option[java.lang.String] = Some(abcabc) scala> condOpt("abc") { case x if x.length == 4 => x + x } res2: Option[java.lang.String] = None 

在function风格中,Scala中的match运算符function最强大。 这意味着,而不是在case陈述“做某事”,你会返回一个有用的价值。 这是一个强制性风格的例子:

 var value:Int = 23 val command:String = ... // we get this from somewhere command match { case "duplicate" => value = value * 2 case "negate" => value = -value case "increment" => value = value + 1 // etc. case _ => // do nothing } println("Result: " + value) 

上面的“无所事事”伤了一点,这是可以理解的,因为这看起来太过分了。 但是,这是由于上述是写在命令式的事实。 虽然这样的构造有时可能是必要的,但在许多情况下,您可以将代码重构为function样式:

 val value:Int = 23 val command:String = ... // we get this from somewhere val result:Int = command match { case "duplicate" => value * 2 case "negate" => -value case "increment" => value + 1 // etc. case _ => value } println("Result: " + result) 

在这种情况下,可以使用整个match语句作为一个值,例如,可以将其分配给一个variables。 而且比较明显的是,无论如何, match语句必须返回一个值。 如果最后一个案例不见了,编译器就不能只做出一些东西。

这是一个味道的问题,但一些开发人员认为这种风格更透明,更容易处理更实际的例子。 我敢打赌,Scala编程语言的发明者在match考虑了更多的function,如果你只需要决定是否需要采取某些行动, if语句就更有意义了。 (另一方面,你也可以使用, if在function的方式,因为它也有一个返回值…)

这可能有助于:

 class Matches(m: Any) { def matches[R](f: PartialFunction[Any, R]) { if (f.isDefinedAt(m)) f(m) } } implicit def any2matches(m: Any) = new Matches(m) scala> 'c' matches { case x: Int => println("Int") } scala> 2 matches { case x: Int => println("Int") } Int 

现在对这个问题的一般性质作一些解释。

比赛会在哪里发生?

有三个模式匹配可能发生的地方: valcasefor 。 他们的规则是:

 // throws an exception if it fails val pattern = value // filters for pattern, but pattern cannot be "identifier: Type", // though that can be replaced by "id1 @ (id2: Type)" for the same effect for (pattern <- object providing map/flatMap/filter/withFilter/foreach) ... // throws an exception if none of the cases match value match { case ... => ... } 

然而,还有另一种情况出现,即函数和部分函数文字。 例如:

 val f: Any => Unit = { case i: Int => println(i) } val pf: PartialFunction[Any, Unit] = { case i: Int => println(i) } 

如果使用与任何case语句不匹配的参数调用,函数和部分函数都会抛出exception。 然而,部分函数还提供了一个名为isDefinedAt的方法,它可以testing是否可以进行匹配,还有一个称为lift的方法,它将PartialFunction[T, R]转换为Function[T, Option[R]] ,这意味着不匹配的值将导致None而不是引发exception。

什么是比赛?

比赛是许多不同testing的组合:

 // assign anything to x case x // only accepts values of type X case x: X // only accepts values matches by pattern case x @ pattern // only accepts a value equal to the value X (upper case here makes a difference) case X // only accepts a value equal to the value of x case `x` // only accept a tuple of the same arity case (x, y, ..., z) // only accepts if extractor(value) returns true of Some(Seq()) (some empty sequence) case extractor() // only accepts if extractor(value) returns Some something case extractor(x) // only accepts if extractor(value) returns Some Seq or Tuple of the same arity case extractor(x, y, ..., z) // only accepts if extractor(value) returns Some Tuple2 or Some Seq with arity 2 case x extractor y // accepts if any of the patterns is accepted (patterns may not contain assignable identifiers) case x | y | ... | z 

现在,提取器是unapplySequnapplySeq方法,第一个返回的BooleanOption[T] ,第二个返回Option[Seq[T]] ,其中None表示不匹配, Some(result)将尝试匹配result如上所述。

所以这里有各种各样的语法select,如果不使用模式匹配可能发生的三种结构之一,就不可能实现。 你可能会模仿一些function,如价值平等和提取,但不是所有的function。

模式也可以用于expression式。 你的代码示例

 a match { case b => // do stuff case _ => // do nothing } 

可以表示为

 for(b <- Some(a)) //do stuff 

诀窍是包装一个使其成为一个有效的枚举。 例如列表(a)也可以工作,但我认为有些(a)最接近你的意图。

我能想到的最好的是这样的:

 def matches[A](a:A)(f:PartialFunction[A, Unit]) = f.isDefinedAt(a) if (matches(a){case ... =>}) { //do stuff } 

这不会赢你任何样式点。

金的答案可以“改善”,以更好地符合您的要求:

 class AnyWrapper[A](wrapped: A) { def matches(f: PartialFunction[A, Unit]) = f.isDefinedAt(wrapped) } implicit def any2wrapper[A](wrapped: A) = new AnyWrapper(wrapped) 

然后:

 val a = "a" :: Nil if (a matches { case "a" :: Nil => }) { println("match") } 

但是,我不会这样做。 => }) {序列在这里真的很难看,整个代码看起来不像普通的匹配那么清晰。 另外,你会得到查找隐式转换的编译时间开销,以及将匹配包装在PartialFunction的运行时开销(不包括与其他已经定义的matches方法可能得到的冲突,就像String )。

为了看起来更好一些(而不是冗长),你可以添加这个def到AnyWrapper

 def ifMatch(f: PartialFunction[A, Unit]): Unit = if (f.isDefinedAt(wrapped)) f(wrapped) 

并像这样使用它:

 a ifMatch { case "a" :: Nil => println("match") } 

这可以节省你的case _ =>行,但如果你想要一个块,而不是一个单一的语句需要双括号…不太好。

请注意,这个构造并不是真正的函数式编程精神,因为它只能用来执行有副作用的东西。 我们不能轻易使用它返回一个值(因此Unit返回值),因为函数是部分的 – 我们需要一个默认值,或者我们可以返回一个Option实例。 但是在这里,我们可能会用一场比赛解开它,所以我们什么也得不到。

坦率地说,你最好习惯于频繁地看到和使用那些match ,并且摆脱这种命令式的结构(遵循Madoc的很好的解释 )。