模式匹配vs if-else
我是Scala的新手。 最近我正在写一个业余爱好的应用程序,并且在很多情况下,试图使用模式匹配而不是if-else。
user.password == enteredPassword match { case true => println("User is authenticated") case false => println("Entered password is invalid") }
代替
if(user.password == enteredPassword) println("User is authenticated") else println("Entered password is invalid")
这些方法是否相同? 出于某种原因,他们中的一个比另一个更可取吗?
class MatchVsIf { def i(b: Boolean) = if (b) 5 else 4 def m(b: Boolean) = b match { case true => 5; case false => 4 } }
我不知道你为什么要使用更长和更笨重的第二个版本。
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
这也是比赛更多的字节码。 即使如此,它也是非常有效的(除非匹配抛出一个错误,这里不会发生),但是对于紧凑性和性能来说, if
/ else
应该是有利的。 但是,如果通过使用匹配来大大提高代码的清晰度,那么请继续(除非您知道性能至关重要,然后您可能想要比较这些差异)。
不要在一个布尔值上模式匹配; 使用一个if-else。
顺便说一句,代码是更好的书面而不重复println
。
println( if(user.password == enteredPassword) "User is authenticated" else "Entered password is invalid" )
一种可能更好的方法是直接对string进行匹配,而不是比较的结果,因为它避免了“布尔盲”。 http://existentialtype.wordpress.com/2011/03/15/boolean-blindness/
一个缺点是需要使用反引号来保护input的密码variables不被镜像。
基本上,你应该尽量避免处理布尔运算,因为它们不能在types层次上传达任何信息。
user.password match { case `enteredPassword` => Right(user) case _ => Left("passwords don't match") }
这两个语句在代码语义方面是等价的。 但是,在一种情况下( match
),编译器可能会创build更复杂(因而效率低下)的代码。
模式匹配通常用于分解更复杂的构造,如多态expression式或将对象解构(不应用)到其组件中。 我不会build议用它作为简单的if-else语句的替代品 – if-else没有任何问题。
请注意,您可以将其用作Scala中的expression式。 因此你可以写
val foo = if(bar.isEmpty) foobar else bar.foo
我为这个愚蠢的例子道歉。
我遇到了同样的问题,并写了testing:
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
对于大多数不是性能敏感的代码,为什么要使用if / else的模式匹配有很多重要的原因:
- 它为每个分支执行一个共同的返回值和types
- 在语言与详尽检查(如斯卡拉),这迫使你明确地考虑所有情况下(和不要你不需要的)
- 它阻止了早期的回报,如果这些回报越来越多,或者分支的长度超过了屏幕的高度(在这个时候它们变得不可见),这就更难以推理了。 有一个额外的缩进级别会警告你,你在一个范围内。
- 它可以帮助您确定要退出的逻辑。 在这种情况下,代码可能已经被重写,并且可以像这样做更干,可debugging和可testing:
val errorMessage = user.password == enteredPassword match { case true => "User is authenticated" case false => "Entered password is invalid" } println(errorMesssage)
这是一个等效的if / else块实现:
var errorMessage = "" if(user.password == enteredPassword) errorMessage = "User is authenticated" else errorMessage = "Entered password is invalid" println(errorMessage)
是的,你可以争辩说,像布尔检查这样简单的事情,你可以使用if-expression。 但是这与这里不相关,并且不能适应超过2个分支的条件。
如果你的更高的关注是可维护性或可读性,模式匹配是真棒,你应该使用它甚至小事情!