tutorial software fungicida examples blanquerna scala

software - scala vs java



Patrón de coincidencia vs if-else (8)

Soy un novato en Scala. Hace poco escribí una aplicación de hobby y me puse a prueba tratando de usar patrones de coincidencia en lugar de if-else en muchos casos.

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

en lugar de

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

¿Son estos enfoques iguales? ¿Es uno de ellos más preferible que otro por alguna razón?


Ambas declaraciones son equivalentes en términos de semántica de código. Pero es posible que el compilador cree un código más complicado (y, por lo tanto, ineficiente) en un caso (la match ).

La coincidencia de patrones se usa generalmente para separar construcciones más complicadas, como expresiones polimórficas o deconstruir unapply (sin unapply ) en sus componentes. No aconsejaría utilizarlo como sustituto de una declaración simple de if-else : no hay nada de malo en if-else .

Tenga en cuenta que puede usarlo como una expresión en Scala. Por lo tanto, puedes escribir

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

Me disculpo por el estúpido ejemplo.


En mi entorno (scala 2.12 y java 8) obtengo resultados diferentes. Match realiza consistentemente mejor en el código anterior:

timeIf: Long = 249 timeMatch: Long = 68


Estoy aquí para ofrecer una opinión diferente: para el ejemplo específico que ofrece, el segundo (si ... más ...) es mejor porque es mucho más fácil de leer.

De hecho, si coloca su primer ejemplo en IntelliJ, le sugerirá que cambie al segundo estilo (si ... más ...). Aquí está la sugerencia de estilo 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 { ??? }


Me encontré con la misma pregunta, y había escrito pruebas:

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


No coincida el patrón en un solo booleano; usa un if-else.

Por cierto, el código está mejor escrito sin duplicar println .

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


Para la gran mayoría del código que no es sensible al rendimiento, hay muchas razones por las que desea utilizar la coincidencia de patrones en if / else:

  • hace cumplir un valor y tipo de devolución común para cada una de sus ramas
  • en idiomas con controles de exhaustividad (como Scala), te obliga a considerar explícitamente todos los casos (y noop los que no necesitas)
  • previene las devoluciones anticipadas, que se vuelven más difíciles de razonar si se multiplican en cascada, o las ramas crecen más que la altura de la pantalla (en ese punto se vuelven invisibles). Tener un nivel extra de sangría te avisará que estás dentro de un alcance.
  • puede ayudarte a identificar la lógica para sacar. En este caso, el código podría haber sido reescrito y hecho más DRY, depurable y comprobable como este:

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

Aquí hay una implementación de bloque if / else equivalente:

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

Sí, puedes argumentar que para algo tan simple como una verificación booleana puedes usar una expresión if. Pero eso no es relevante aquí y no se adapta bien a las condiciones con más de 2 ramas.

Si su mayor preocupación es la facilidad de mantenimiento o legibilidad, la coincidencia de patrones es increíble y debe usarlo incluso para cosas menores.


Una forma posiblemente mejor sería emparejar patrones en la cadena directamente, no en el resultado de la comparación, ya que evita la "ceguera booleana". http://existentialtype.wordpress.com/2011/03/15/boolean-blindness/

Una desventaja es la necesidad de usar comillas inversas para proteger la variable de contraseña introducida de la sombra.

Básicamente, debe evitar comerciar con booleanos tanto como sea posible, ya que no transmiten ninguna información al nivel de tipo.

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


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

No estoy seguro de por qué querría utilizar la segunda versión más larga y clunkier.

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

Y eso es mucho más bytecode para el partido también. Es bastante eficiente incluso (no hay boxeo a menos que el partido arroje un error, que no puede suceder aquí), pero para compacidad y rendimiento uno debería favorecer a if / else . Sin embargo, si mejora la claridad de su código mediante el uso de coincidencias, continúe (excepto en aquellos casos excepcionales en los que sabe que el rendimiento es crítico, y luego es posible que desee comparar la diferencia).