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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4437061/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 02:38:57  来源:igfitidea点击:

Scala: short form of pattern matching that returns Boolean

scalapattern-matching

提问by Vilius Normantas

I found myself writing something like this quite often:

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

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

Is there a shorter way to check if some value matches a pattern? I mean, in this case I could just write if (a == b) // do stuff, but what if the pattern is more complex? Like when matching against a list or any pattern of arbitrary complexity. I'd like to be able to write something like this:

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

if (a matches b) // do stuff

I'm relatively new to Scala, so please pardon, if I'm missing something big :)

我对 Scala 比较陌生,所以请原谅,如果我遗漏了一些重要的东西:)

回答by psp

This is exactly why I wrote these functions, which are apparently impressively obscure since nobody has mentioned them.

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

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

回答by Madoc

The matchoperator in Scala is most powerful when used in functional style. This means, rather than "doing something" in the casestatements, you would return a useful value. Here is an example for an imperative style:

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)

It is very understandable that the "do nothing" above hurts a little, because it seems superflous. However, this is due to the fact that the above is written in imperative style. While constructs like these may sometimes be necessary, in many cases you can refactor your code to functional style:

上面的“什么都不做”有点伤人,这是非常可以理解的,因为它看起来是多余的。然而,这是因为上述内容是以命令式风格编写的。虽然有时可能需要像这样的构造,但在许多情况下,您可以将代码重构为函数式风格:

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)

In this case, you use the whole matchstatement as a value that you can, for example, assign to a variable. And it is also much more obvious that the matchstatement must return a value in any case; if the last case would be missing, the compiler could not just make something up.

在这种情况下,您可以将整个match语句用作一个值,例如,您可以将其分配给一个变量。更明显的是,该match语句在任何情况下都必须返回一个值;如果缺少最后一种情况,编译器就不能随便编造一些东西。

It is a question of taste, but some developers consider this style to be more transparent and easier to handle in more real-world examples. I would bet that the inventors of the Scala programming language had a more functional use in mind for match, and indeed the ifstatement makes more sense if you only need to decide whether or not a certain action needs to be taken. (On the other hand, you can also use ifin the functional way, because it also has a return value...)

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

回答by Daniel C. Sobral

This might help:

这可能有帮助:

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

Now, some explanation on the general nature of the problem.

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

Where may a match happen?

哪里可能会发生比赛?

There are three places where pattern matching might happen: val, caseand for. The rules for them are:

模式匹配可能发生在三个地方: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 ... => ... }

There is, however, another situation where casemight appear, which is function and partial function literals. For example:

然而,还有另一种case可能出现的情况,即函数字面量和部分函数字面量。例如:

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

Both functions and partial functions will throw an exception if called with an argument that doesn't match any of the case statements. However, partial functions also provide a method called isDefinedAtwhich can test whether a match can be made or not, as well as a method called lift, which will turn a PartialFunction[T, R]into a Function[T, Option[R]], which means non-matching values will result in Noneinstead of throwing an exception.

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

What is a match?

什么是比赛?

A match is a combination of many different tests:

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

// 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

Now, extractors are the methods unapplyor unapplySeq, the first returning Booleanor Option[T], and the second returning Option[Seq[T]], where Nonemeans no match is made, and Some(result)will try to match resultas described above.

现在,提取器是方法unapplyor unapplySeq,第一个返回Booleanor Option[T],第二个返回Option[Seq[T]],其中None意味着没有匹配,Some(result)并将尝试result如上所述匹配。

So there are all kinds of syntactic alternatives here, which just aren't possible without the use of one of the three constructions where pattern matches may happen. You may able to emulate some of the features, like value equality and extractors, but not all of them.

所以这里有各种各样的句法替代方案,如果不使用可能发生模式匹配的三种结构之一,这是不可能的。您可以模拟某些功能,例如值相等和提取器,但不是全部。

回答by Maarten Bynens

Patterns can also be used in for expressions. Your code sample

模式也可用于表达式。您的代码示例

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

can then be expressed as

那么可以表示为

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

The trick is to wrap a to make it a valid enumerator. E.g. List(a) would also work, but I think Some(a) is closest to your intended meaning.

诀窍是包装 a 以使其成为有效的枚举数。例如 List(a) 也可以,但我认为 Some(a) 最接近您的预期含义。

回答by Kim Stebel

The best I can come up with is this:

我能想到的最好的是:

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

if (matches(a){case ... =>}) {
    //do stuff
}

This won't win you any style points though.

但这不会为您赢得任何风格点数。

回答by Jean-Philippe Pellet

Kim's answercan be “improved” to better match your requirement:

Kim 的回答可以“改进”以更好地满足您的要求:

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

then:

然后:

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

I wouldn't do it, however. The => }) {sequence is really ugly here, and the whole code looks much less clear than a normal match. Plus, you get the compile-time overhead of looking up the implicit conversion, and the run-time overhead of wrapping the match in a PartialFunction(not counting the conflicts you could get with other, already defined matchesmethods, like the one in String).

然而,我不会这样做。这里的=> }) {序列真的很难看,整个代码看起来比正常匹配要清晰得多。此外,您还会获得查找隐式转换的编译时开销,以及将匹配项包装在 a 中的运行时开销PartialFunction(不计算您可能与其他已定义matches方法(如 中的方法)发生的冲突String)。

To look a little bit better (and be less verbose), you could add this def to AnyWrapper:

为了看起来更好一点(并且不那么冗长),您可以将此 def 添加到AnyWrapper

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

and use it like this:

并像这样使用它:

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

which saves you your case _ =>line, but requires double braces if you want a block instead of a single statement... Not so nice.

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

Note that this construct is not really in the spirit of functional programming, as it can only be used to execute something that has side effects. We can't easily use it to return a value (therefore the Unitreturn value), as the function is partial — we'd need a default value, or we could return an Optioninstance. But here again, we would probably unwrap it with a match, so we'd gain nothing.

请注意,此构造并不真正符合函数式编程的精神,因为它只能用于执行具有副作用的内容。我们不能轻易地使用它来返回一个值(因此是Unit返回值),因为该函数是部分的——我们需要一个默认值,或者我们可以返回一个Option实例。但在这里,我们可能会用火柴打开它,所以我们一无所获。

Frankly, you're better off getting used to seeing and using those matchfrequently, and moving away from this kind of imperative-style constructs (following Madoc's nice explanation).

坦率地说,你最好习惯于match经常看到和使用它们,并远离这种命令式风格的构造(遵循Madoc 的很好的解释)。