class MatchVsIf {
def i(b: Boolean) = if (b) 5 else 4
def m(b: Boolean) = b match { case true => 5; case false => 4 }
}
Ich bin mir nicht sicher, warum Sie die längere und klobigere zweite Version verwenden möchten.
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;
21: dup
22: iload_2
23: invokestatic #20;
26: invokespecial #24;
29: athrow
Und das ist auch viel mehr Bytecode für das Match. Trotzdem ist es ziemlich effizient (es gibt kein Boxen, es sei denn, das Match wirft einen Fehler auf, der hier nicht auftreten kann), aber für Kompaktheit und Leistung sollte man if
/ bevorzugen else
. Wenn die Klarheit Ihres Codes durch die Verwendung von Übereinstimmungen erheblich verbessert wird, fahren Sie fort (außer in den seltenen Fällen, in denen Sie wissen, dass die Leistung kritisch ist, und dann möchten Sie möglicherweise den Unterschied vergleichen).
Nicht Musterübereinstimmung auf einem einzelnen Booleschen Wert; benutze ein if-else.
Übrigens ist der Code besser geschrieben, ohne zu duplizieren
println
.println( if(user.password == enteredPassword) "User is authenticated" else "Entered password is invalid" )
quelle
Ein wohl besserer Weg wäre, die Musterübereinstimmung direkt auf der Zeichenfolge und nicht auf dem Ergebnis des Vergleichs vorzunehmen, da dadurch "boolesche Blindheit" vermieden wird. http://existentialtype.wordpress.com/2011/03/15/boolean-blindness/
Ein Nachteil ist die Notwendigkeit, Backquotes zu verwenden, um die Variable enterPassword vor Schatten zu schützen.
Grundsätzlich sollten Sie vermeiden, sich so weit wie möglich mit Booleschen Werten zu befassen, da diese keine Informationen auf Typebene vermitteln.
user.password match { case `enteredPassword` => Right(user) case _ => Left("passwords don't match") }
quelle
Beide Anweisungen sind hinsichtlich der Codesemantik äquivalent. Es ist jedoch möglich, dass der Compiler in einem Fall (dem
match
) komplizierteren (und damit ineffizienten) Code erstellt .Pattern Matching wird normalerweise verwendet, um kompliziertere Konstrukte wie polymorphe Ausdrücke oder das Dekonstruieren von
unapply
Objekten in ihre Komponenten zu zerlegen. Ich würde nicht raten, es als Ersatz für eine einfache if-else- Aussage zu verwenden - es ist nichts falsch mit if-else .Beachten Sie, dass Sie es als Ausdruck in Scala verwenden können. So können Sie schreiben
val foo = if(bar.isEmpty) foobar else bar.foo
Ich entschuldige mich für das dumme Beispiel.
quelle
Für die große Mehrheit des Codes, der nicht leistungsabhängig ist, gibt es viele gute Gründe, warum Sie den Mustervergleich verwenden möchten, wenn / sonst:
val errorMessage = user.password == enteredPassword match { case true => "User is authenticated" case false => "Entered password is invalid" } println(errorMesssage)
Hier ist eine äquivalente if / else-Blockimplementierung:
var errorMessage = "" if(user.password == enteredPassword) errorMessage = "User is authenticated" else errorMessage = "Entered password is invalid" println(errorMessage)
Ja, Sie können argumentieren, dass Sie für etwas so Einfaches wie eine boolesche Prüfung einen if-Ausdruck verwenden können. Dies ist hier jedoch nicht relevant und lässt sich nicht gut auf Bedingungen mit mehr als 2 Zweigen skalieren.
Wenn Ihr Hauptanliegen die Wartbarkeit oder Lesbarkeit ist, ist der Mustervergleich fantastisch und Sie sollten ihn auch für kleinere Dinge verwenden!
quelle
val errorMessage = if (user.password == enteredPassword) "User is authenticated" else "Entered password is invalid"
val k = if (false) "1" else if (false) "2" else "3"
Ich bin auf dieselbe Frage gestoßen und hatte Tests geschrieben:
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
quelle
System.currentTimeMillis()
hat schreckliche Präzision;System.nanoTime
ist in der Regel besser. Trotzdem sollten Sie die Auswirkungen der JIT-Kompilierung, der Speicherbereinigung usw. entfernen. Verwenden Sie am besten ein Mikro-Benchmarking-Tool (wie ScalaMeter , um beide Ansätze richtig zu bewerten).Ich bin hier, um eine andere Meinung zu vertreten: Für das spezifische Beispiel, das Sie anbieten, ist der zweite (wenn ... sonst ...) Stil tatsächlich besser, weil er viel einfacher zu lesen ist.
Wenn Sie Ihr erstes Beispiel in IntelliJ einfügen, wird empfohlen, zum zweiten (wenn ... sonst ...) Stil zu wechseln. Hier ist der Vorschlag für den IntelliJ-Stil:
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 { ??? }
quelle
Es ist 2020, der Scala-Compiler generiert im Pattern-Matching-Fall einen weitaus effizienteren Bytecode. Die Leistungskommentare in der akzeptierten Antwort sind im Jahr 2020 irreführend.
Der durch Musterübereinstimmung generierte Bytecode stellt eine harte Konkurrenz zu if-else-Gewinnern dar, die zuweilen viel bessere und konsistentere Ergebnisse erzielen.
Man kann Pattern Match oder If-else verwenden, je nach Situation und Einfachheit. Der Mustervergleich hat jedoch eine schlechte Leistung. Die Schlussfolgerung ist nicht mehr gültig.
Sie können das folgende Snippet ausprobieren und die Ergebnisse anzeigen:
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
PS: Da die Zahlen nanospezifisch sind, stimmen die Ergebnisse möglicherweise nicht genau mit den exakten Zahlen überein, aber das Argument zur Leistung gilt.
quelle
In meiner Umgebung (Scala 2.12 und Java 8) erhalte ich unterschiedliche Ergebnisse. Match schneidet im obigen Code durchweg besser ab:
timeIf: Long = 249 timeMatch: Long = 68
quelle