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

Vil*_*tas 42 scala pattern-matching

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

a match {     
  case `b` => // do stuff
  case _ => // do nothing
}
Run Code Online (Sandbox Code Playgroud)

是否有更短的方法来检查某个值是否与模式匹配?我的意思是,在这种情况下我可以写if (a == b) // do stuff,但如果模式更复杂怎么办?就像匹配列表或任意复杂的任何模式时一样.我希望能够写出这样的东西:

if (a matches b) // do stuff
Run Code Online (Sandbox Code Playgroud)

我对Scala比较陌生,所以请原谅,如果我错过了大的东西:)

psp*_*psp 63

这正是我写这些函数的原因,因为没有人提到它们,这些函数显然令人印象深刻.

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


Mad*_*doc 12

matchScala中的运算符在功能样式中使用时功能最强大.这意味着,不是在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)
Run Code Online (Sandbox Code Playgroud)

可以理解的是,上面的"无所事事"有点伤害,因为它似乎是超级丰富的.然而,这是因为以上是以命令式的方式写的.虽然这些结构有时可能是必要的,但在许多情况下,您可以将代码重构为功能样式:

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

在这种情况下,您可以将整个match语句用作值,例如,可以将其赋值给变量.并且更明显的是,match声明必须在任何情况下返回值; 如果最后一种情况会丢失,编译器就不能只做出一些事情.

这是一个品味问题,但一些开发人员认为这种风格更透明,更容易处理更真实的例子.我敢打赌,Scala编程语言的发明者在考虑更多功能时使用,如果你只需要决定是否需要采取某种行动,那么match这种if说法确实更有意义.(另一方面,你也可以if在功能方面使用,因为它也有一个返回值......)


Dan*_*ral 12

这可能有所帮助:

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

现在,对问题的一般性质进行一些解释.

匹配发生在哪里?

有三个地方,模式匹配可能发生:val,casefor.它们的规则是:

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

然而,还有另一种case可能出现的情况,即功能和部分功能文字.例如:

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

如果使用与任何case语句都不匹配的参数调用,函数和部分函数都将抛出异常.但是,部分函数还提供了一个方法isDefinedAt,可以测试是否可以进行匹配,以及调用的方法lift,它将a PartialFunction[T, R]转换为a Function[T, Option[R]],这意味着将导致非匹配值None而不是抛出异常.

什么是匹配?

匹配是许多不同测试的组合:

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

现在,提取器是方法,unapply或者unapplySeq第一个返回,Boolean或者Option[T]第二个返回Option[Seq[T]],这None意味着没有匹配,Some(result)并将尝试匹配result,如上所述.

因此,这里有各种语法替代方案,如果不使用模式匹配可能发生的三种结构中的一种,这是不可能的.您可以模拟一些功能,例如值相等和提取器,但不是所有功能.


Maa*_*ens 7

模式也可用于表达式.你的代码示例

a match {     
  case b => // do stuff
  case _ => // do nothing
}
Run Code Online (Sandbox Code Playgroud)

然后可以表达为

for(b <- Some(a)) //do stuff
Run Code Online (Sandbox Code Playgroud)

诀窍是包装一个使其成为有效的枚举器.例如,列表(a)也可以使用,但我认为某些(a)最接近您的预期含义.

  • @Collin嗯,你不能使用_any_模式,但唯一不起作用的模式,`id:Type`,可以用`idtmp @(id:Type)`代替相同的效果. (2认同)

Kim*_*bel 5

我能想到的最好的是:

def matches[A](a:A)(f:PartialFunction[A, Unit]) = f.isDefinedAt(a)

if (matches(a){case ... =>}) {
    //do stuff
}
Run Code Online (Sandbox Code Playgroud)

这不会赢得任何风格点.