scala 模式匹配 vs if-else

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9266822/
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 03:52:39  来源:igfitidea点击:

Pattern matching vs if-else

scala

提问by Soteric

I'm novice in Scala. Recently I was writing a hobby app and caught myself trying to use pattern matching instead of if-else in many cases.

我是 Scala 的新手。最近我正在编写一个业余爱好应用程序,并发现自己在许多情况下试图使用模式匹配而不是 if-else。

user.password == enteredPassword match {
  case true => println("User is authenticated")
  case false => println("Entered password is invalid")
}

instead of

代替

if(user.password == enteredPassword)
  println("User is authenticated")
else
  println("Entered password is invalid")

Are these approaches equal? Is one of them more preferrable than another for some reason?

这些方法是否相同?出于某种原因,其中一个比另一个更受欢迎吗?

回答by Rex Kerr

class MatchVsIf {
  def i(b: Boolean) = if (b) 5 else 4
  def m(b: Boolean) = b match { case true => 5; case false => 4 }
}

I'm not sure why you'd want to use the longer and clunkier second version.

我不确定您为什么要使用更长、更笨拙的第二个版本。

scala> :javap -cp MatchVsIf
Compiled from "<console>"
public class MatchVsIf extends java.lang.Object implements scala.ScalaObject{
public int i(boolean);
  Code:
   0:   iload_1
   1:   ifeq    8
   4:   iconst_5
   5:   goto    9
   8:   iconst_4
   9:   ireturn

public int m(boolean);
  Code:
   0:   iload_1
   1:   istore_2
   2:   iload_2
   3:   iconst_1
   4:   if_icmpne   11
   7:   iconst_5
   8:   goto    17
   11:  iload_2
   12:  iconst_0
   13:  if_icmpne   18
   16:  iconst_4
   17:  ireturn
   18:  new #14; //class scala/MatchError
   21:  dup
   22:  iload_2
   23:  invokestatic    #20; //Method scala/runtime/BoxesRunTime.boxToBoolean:(Z)Ljava/lang/Boolean;
   26:  invokespecial   #24; //Method scala/MatchError."<init>":(Ljava/lang/Object;)V
   29:  athrow

And that's a lot more bytecode for the match also. It's fairlyefficient even so (there's no boxing unless the match throws an error, which can't happen here), but for compactness and performance one should favor if/else. If the clarity of your code is greatly improved by using match, however, go ahead (except in those rare cases where you know performance is critical, and then you might want to compare the difference).

这也是比赛的更多字节码。即便如此,它也是相当有效的(除非比赛抛出错误,否则没有拳击,这在这里不会发生),但为了紧凑性和性能,应该支持if/ else。但是,如果通过使用 match 大大提高了代码的清晰度,请继续(除非在您知道性能至关重要的极少数情况下,然后您可能想要比较差异)。

回答by retronym

Don't pattern match on a single boolean; use an if-else.

不要对单个布尔值进行模式匹配;使用 if-else。

Incidentally, the code is better written without duplicating println.

顺便说一句,代码最好不要重复编写println

println(
  if(user.password == enteredPassword) 
    "User is authenticated"
  else 
    "Entered password is invalid"
)

回答by Clément D.

One arguably better way would be to pattern match on the string directly, not on the result of the comparison, as it avoids "boolean blindness". http://existentialtype.wordpress.com/2011/03/15/boolean-blindness/

一种可以说更好的方法是直接在字符串上进行模式匹配,而不是在比较结果上进行模式匹配,因为它避免了“布尔盲”。http://existentialtype.wordpress.com/2011/03/15/boolean-blindness/

One downside is the need to use backquotes to protect the enteredPassword variable from being shadowed.

一个缺点是需要使用反引号来保护 enterPassword 变量不被隐藏。

Basically, you should tend to avoid dealing with booleans as much as possible, as they don't convey any information at the type level.

基本上,您应该尽量避免处理布尔值,因为它们不会在类型级别传达任何信息。

user.password match {
    case `enteredPassword` => Right(user)
    case _ => Left("passwords don't match")
}

回答by ziggystar

Both statements are equivalent in terms of code semantics. But it might be possible that the compiler creates more complicated (and thus inefficient) code in one case (the match).

这两个语句在代码语义方面是等效的。但在一种情况下(match),编译器可能会创建更复杂(因此效率低下)的代码。

Pattern matching is usually used to break apart more complicated constructs, like polymorphic expressions or deconstructing (unapplying) objects into their components. I would not advice to use it as a surrogate for a simple if-elsestatement - there's nothing wrong with if-else.

模式匹配通常用于分解更复杂的构造,例如多态表达式或将 ( unapplying) 对象解构为它们的组件。我不会建议使用它作为替代一个简单的if-else语句-有没有错的if-else

Note that you can use it as an expression in Scala. Thus you can write

请注意,您可以将其用作 Scala 中的表达式。这样你就可以写

val foo = if(bar.isEmpty) foobar else bar.foo

I apologize for the stupid example.

我为这个愚蠢的例子道歉。

回答by Kevin Li

For the large majority of code that isn't performance-sensitive, there are a lot of great reasons why you'd want to use pattern matching over if/else:

对于大多数对性能不敏感的代码,有很多很好的理由让您想在 if/else 上使用模式匹配:

  • it enforces a common return value and type for each of your branches
  • in languages with exhaustiveness checks (like Scala), it forces you to explicitly consider all cases (and noop the ones you don't need)
  • it prevents early returns, which become harder to reason if they cascade, grow in number, or the branches grow longer than the height of your screen (at which point they become invisible). Having an extra level of indentation will warn you you're inside a scope.
  • it can help you identify logic to pull out. In this case the code could have been rewritten and made more DRY, debuggable, and testable like this:
  • 它为您的每个分支强制执行公共返回值和类型
  • 在具有详尽性检查的语言(如 Scala)中,它迫使您明确考虑所有情况(并忽略您不需要的情况)
  • 它可以防止过早返回,如果它们级联、数量增加或分支长得超过屏幕的高度(此时它们变得不可见),就会变得更难推理。有一个额外的缩进级别会警告你你在一个范围内。
  • 它可以帮助您确定要退出的逻辑。在这种情况下,可以重写代码并使其更加 DRY、可调试和可测试,如下所示:
val errorMessage = user.password == enteredPassword match {
  case true => "User is authenticated"
  case false => "Entered password is invalid"
}

println(errorMesssage)

Here's an equivalent if/else block implementation:

这是一个等效的 if/else 块实现:

var errorMessage = ""

if(user.password == enteredPassword)
  errorMessage = "User is authenticated"
else
  errorMessage = "Entered password is invalid"

println(errorMessage)

Yes, you can argue that for something as simple as a boolean check you can use an if-expression. But that's not relevant here and doesn't scale well to conditions with more than 2 branches.

是的,您可以争辩说,对于像布尔检查这样简单的事情,您可以使用 if 表达式。但这与此处无关,并且不适用于具有 2 个以上分支的条件。

If your higher concern is maintainability or readability, pattern matching is awesome and you should use it for even minor things!

如果您更关心可维护性或可读性,那么模式匹配非常棒,您应该将它用于即使是次要的事情!

回答by Andrey

I'v came across same question, and had written tests:

我遇到了同样的问题,并进行了笔试:

     def factorial(x: Int): Int = {
        def loop(acc: Int, c: Int): Int = {
          c match {
            case 0 => acc
            case _ => loop(acc * c, c - 1)
          }
        }
        loop(1, x)
      }

      def factorialIf(x: Int): Int = {
        def loop(acc: Int, c: Int): Int = 
            if (c == 0) acc else loop(acc * c, c - 1)
        loop(1, x)
      }

    def measure(e: (Int) => Int, arg:Int, numIters: Int): Long = {
        def loop(max: Int): Unit = {
          if (max == 0)
            return
          else {
            val x = e(arg)
            loop(max-1)
          }
        }

        val startMatch = System.currentTimeMillis()
        loop(numIters)
        System.currentTimeMillis() - startMatch
      }                  
val timeIf = measure(factorialIf, 1000,1000000)
val timeMatch = measure(factorial, 1000,1000000)

timeIf : Long = 22 timeMatch : Long = 1092

timeIf : Long = 22 timeMatch : Long = 1092

回答by Yang

I am here to offer a different opinion: For the specific example you offer, the second one (if...else...) style is actually better because it is much easier to read.

我在这里提供不同的意见:对于您提供的具体示例,第二种(if...else...)样式实际上更好,因为它更容易阅读。

In fact, if you put your first example into IntelliJ, it will suggest you to change to the second (if...else...) style. Here is the IntelliJ style suggestion:

事实上,如果您将第一个示例放入 IntelliJ,它会建议您更改为第二个 (if.​​..else...) 样式。这是 IntelliJ 风格的建议:

Trivial match can be simplified less... (?F1) 

Suggests to replace trivial pattern match on a boolean expression with a conditional statement.
Before:
    bool match {
      case true => ???
      case false => ???
    }
After:
    if (bool) {
      ???
    } else {
      ???
    }

回答by Frank Eggink

In my environment (scala 2.12 and java 8) I get different results. Match performs consistently better in the code above:

在我的环境(scala 2.12 和 java 8)中,我得到了不同的结果。Match 在上面的代码中始终表现得更好:

timeIf: Long = 249 timeMatch: Long = 68

时间如果:长 = 249 时间匹配:长 = 68