我是斯卡拉的新手.最近我写了一个爱好应用程序,并发现自己试图在许多情况下使用模式匹配而不是if-else.
user.password == enteredPassword match {
case true => println("User is authenticated")
case false => println("Entered password is invalid")
}
Run Code Online (Sandbox Code Playgroud)
代替
if(user.password == enteredPassword)
println("User is authenticated")
else
println("Entered password is invalid")
Run Code Online (Sandbox Code Playgroud)
这些方法是否相同?出于某种原因,其中一个比另一个更受欢迎吗?
Rex*_*err 94
class MatchVsIf {
def i(b: Boolean) = if (b) 5 else 4
def m(b: Boolean) = b match { case true => 5; case false => 4 }
}
Run Code Online (Sandbox Code Playgroud)
我不确定你为什么要使用更长更笨的第二版.
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
Run Code Online (Sandbox Code Playgroud)
这也是匹配的字节码.它即使如此也是相当有效的(除非匹配引发错误,否则没有拳击,这在这里不会发生),但是对于紧凑性和性能,我们应该支持if
/ else
.但是,如果使用匹配大大提高了代码的清晰度,请继续(除非在您知道性能至关重要的极少数情况下,然后您可能希望比较差异).
ret*_*nym 29
不要在单个布尔值上进行模式匹配; 使用if-else.
顺便说一句,代码更好地编写而不重复println
.
println(
if(user.password == enteredPassword)
"User is authenticated"
else
"Entered password is invalid"
)
Run Code Online (Sandbox Code Playgroud)
小智 15
一种可以说是更好的方法是直接对字符串进行模式匹配,而不是比较结果,因为它避免了"布尔盲".http://existentialtype.wordpress.com/2011/03/15/boolean-blindness/
一个缺点是需要使用反引号来保护enteredPassword变量不被遮蔽.
基本上,您应该尽可能避免处理布尔值,因为它们不会在类型级别传达任何信息.
user.password match {
case `enteredPassword` => Right(user)
case _ => Left("passwords don't match")
}
Run Code Online (Sandbox Code Playgroud)
zig*_*tar 11
两个语句在代码语义方面都是等价的.但是,编译器可能会在一种情况下创建更复杂(因此效率低下)的代码(match
).
模式匹配通常用于分解更复杂的构造,例如多态表达式或将unapply
对象解构为其组件.我不会建议,使用它作为替代一个简单的if-else语句-有没有错的if-else.
请注意,您可以将其用作Scala中的表达式.因此你可以写
val foo = if(bar.isEmpty) foobar else bar.foo
Run Code Online (Sandbox Code Playgroud)
我为这个愚蠢的例子道歉.
小智 10
到了 2020 年,Scala 编译器在模式匹配情况下生成了更加高效的字节码。已接受答案中的绩效评论在 2020 年具有误导性。
模式匹配生成的字节代码与 if-else 进行了激烈的竞争,有时模式匹配会获胜,从而提供更好且一致的结果。
人们可以根据情况和简单性使用模式匹配或 if-else。但模式匹配性能较差的结论不再有效。
您可以尝试以下代码片段并查看结果:
def testMatch(password: String, enteredPassword: String) = {
val entering = System.nanoTime()
password == enteredPassword match {
case true => {
println(s"User is authenticated. Time taken to evaluate True in match : ${System.nanoTime() - entering}"
)
}
case false => {
println(s"Entered password is invalid. Time taken to evaluate false in match : ${System.nanoTime() - entering}"
)
}
}
}
testMatch("abc", "abc")
testMatch("abc", "def")
Pattern Match Results :
User is authenticated. Time taken to evaluate True in match : 1798
Entered password is invalid. Time taken to evaluate false in match : 3878
If else :
def testIf(password: String, enteredPassword: String) = {
val entering = System.nanoTime()
if (password == enteredPassword) {
println(
s"User is authenticated. Time taken to evaluate if : ${System.nanoTime() - entering}"
)
} else {
println(
s"Entered password is invalid.Time taken to evaluate else ${System.nanoTime() - entering}"
)
}
}
testIf("abc", "abc")
testIf("abc", "def")
If-else time results:
User is authenticated. Time taken to evaluate if : 65062652
Entered password is invalid.Time taken to evaluate else : 1809
Run Code Online (Sandbox Code Playgroud)
PS:由于这些数字是纳米精度,因此结果可能无法与确切的数字精确匹配,但关于性能的争论仍然有效。
对于大多数对性能不敏感的代码,有很多很好的理由说明为什么要在if/else上使用模式匹配:
val errorMessage = user.password == enteredPassword match {
case true => "User is authenticated"
case false => "Entered password is invalid"
}
println(errorMesssage)
Run Code Online (Sandbox Code Playgroud)
这是一个等效的if/else块实现:
var errorMessage = ""
if(user.password == enteredPassword)
errorMessage = "User is authenticated"
else
errorMessage = "Entered password is invalid"
println(errorMessage)
Run Code Online (Sandbox Code Playgroud)
是的,您可以争辩说,对于像布尔检查这样简单的事情,您可以使用if-expression.但这在这里并不重要,并且不能很好地适应具有2个以上分支的条件.
如果您的高度关注是可维护性或可读性,那么模式匹配非常棒,您应该将它用于甚至是小事情!