-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Inclusión de la parte 4 - pattern-matching - tanto teoría como ejerci…
…cios.
- Loading branch information
david
committed
Jun 30, 2022
1 parent
99d24bd
commit f504701
Showing
6 changed files
with
607 additions
and
0 deletions.
There are no files selected for viewing
37 changes: 37 additions & 0 deletions
37
src/main/scala/ejercicios/parte4Patrones/PatternMatchingEnunciado.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package ejercicios.parte4Patrones | ||
|
||
import lecciones.parte4Patrones.PatronesDondeNoLosEsperas | ||
|
||
|
||
/** | ||
* Ejercicio del bloque teórico de Patter Matching [[PatronesDondeNoLosEsperas]]. | ||
* | ||
* Construir un método del tipo Expresión => String. | ||
* El String es una representación de como una persona escribiría una operación de suma y multiplicación. | ||
* Por ejemplo: | ||
* - 3 por 5 más 2 => 3 * 5 + 2 | ||
* - 2 + 4 y después por 5 => (2 + 4) * 5 | ||
* | ||
* Solución disponible en [[PatternMatchingSolucion]]. | ||
*/ | ||
object PatternMatchingEnunciado { | ||
|
||
def main(args: Array[String]): Unit = { | ||
val test_1 = Suma(Multiplicación(Número(2), Número(1)), Número(3)) | ||
//Debería resultar en 2 * 1 + 3 | ||
val test_2 = Suma(Multiplicación(Suma(Número(3), Número(2)), Número(4)), Multiplicación(Número(1), Número(2))) | ||
// Debería resultar en (3 + 2) * 4 + 1 * 2 | ||
} | ||
|
||
sealed trait Expresión | ||
|
||
case class Número(n: Int) extends Expresión | ||
|
||
case class Suma(e1: Expresión, e2: Expresión) extends Expresión | ||
|
||
case class Multiplicación(e1: Expresión, e2: Expresión) extends Expresión | ||
|
||
def transcripciónHumana(expresión: Expresión): String = ??? | ||
|
||
|
||
} |
69 changes: 69 additions & 0 deletions
69
src/main/scala/ejercicios/parte4Patrones/PatternMatchingSolucion.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package ejercicios.parte4Patrones | ||
|
||
import lecciones.parte4Patrones.PatronesDondeNoLosEsperas | ||
|
||
|
||
/** | ||
* Solución de [[PatternMatchingEnunciado]]. | ||
*/ | ||
object PatternMatchingSolucion { | ||
|
||
def main(args: Array[String]): Unit = { | ||
println(transcripciónHumana(Suma(Multiplicación(Número(2), Número(1)), Número(3)))) | ||
println(transcripciónHumana(Suma(Multiplicación(Suma(Número(3), Número(2)), Número(4)), Multiplicación(Número(1), Número(2))))) | ||
} | ||
|
||
sealed trait Expresión | ||
|
||
case class Número(n: Int) extends Expresión | ||
|
||
case class Suma(e1: Expresión, e2: Expresión) extends Expresión | ||
|
||
case class Multiplicación(e1: Expresión, e2: Expresión) extends Expresión | ||
|
||
def transcripciónHumana(expresión: Expresión): String = { | ||
// Una solución | ||
método(expresión) | ||
// Otra solución | ||
función(expresión) | ||
} | ||
|
||
/** | ||
* Definido como una función | ||
*/ | ||
val función: Expresión => String = { | ||
case Número(n) => n.toString | ||
case Suma(e1, e2) => s"${función(e1)} + ${función(e2)}" | ||
case Multiplicación(e1, e2) => | ||
val añadeParéntesis: Expresión => String = { | ||
case e@Multiplicación(_, _) => función(e) | ||
case e@Número(_) => función(e) | ||
case e => s"(${función(e)})" | ||
} | ||
s"${añadeParéntesis(e1)} * ${añadeParéntesis(e2)}" | ||
|
||
} | ||
|
||
/** | ||
* Definido como un método | ||
* @param expresión expresión en clases | ||
* @return expresión humana | ||
*/ | ||
def método(expresión: Expresión): String = { | ||
// Método auxiliar para los paréntesis | ||
def quizásAñadeParéntesis(expr: Expresión) = expr match { | ||
case Multiplicación(_, _) => método(expr) | ||
case Número(_) => método(expr) | ||
case _ => "(" + método(expr) + ")" | ||
} | ||
|
||
expresión match { | ||
case Número(n) => n.toString | ||
case Suma(e1, e2) => método(e1) + " + " + método(e2) | ||
case Multiplicación(e1, e2) => quizásAñadeParéntesis(e1) + " * " + quizásAñadeParéntesis(e2) | ||
} | ||
} | ||
|
||
|
||
|
||
} |
71 changes: 71 additions & 0 deletions
71
src/main/scala/lecciones/parte4Patrones/PatronesDondeNoLosEsperas.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
|
||
package lecciones.parte4Patrones | ||
|
||
import ejercicios.parte4Patrones.PatternMatchingEnunciado | ||
|
||
/** | ||
* Lección 3 de 4. | ||
* | ||
* El Pattern Matching está presente dentro del propio lenguaje y que habilita muchas de las sintaxis dulces. | ||
* Ejercicios en [[PatternMatchingEnunciado]]. | ||
*/ | ||
object PatronesDondeNoLosEsperas extends App { | ||
|
||
/** | ||
* Los bloques de código try - catch - finally es pattern matching | ||
*/ | ||
try { | ||
println(10 / 0) | ||
} catch { | ||
case r: RuntimeException => println("error durante ejecución") | ||
case npe: NullPointerException => println("puntero nulo") | ||
case _ => println("otra cosa") | ||
} | ||
|
||
/** | ||
* Las for - comprehensions utilizan la descomposición de case clases y los if de seguridad (if guards) | ||
*/ | ||
|
||
val listaDeNúmeros = List(1, 2, 3, 4, 5, 6) | ||
val númerosImpares = for { | ||
x <- listaDeNúmeros if x % 2 == 0 | ||
} yield 10 * x | ||
println(númerosImpares) | ||
|
||
/** | ||
* La asignación de valores a val definidas al mismo tiempo también usa patter matching | ||
*/ | ||
val tupla123 = (1, 2, 3) | ||
val (uno, dos, tres) = tupla123 | ||
println(uno) | ||
println(dos) | ||
println(tres) | ||
|
||
/** | ||
* La descomposición de una lista en cabeza y cola sigue la misma lógica que con los valores asignados a val | ||
*/ | ||
val cabeza :: cola = listaDeNúmeros | ||
println(cabeza) | ||
println(cola) | ||
|
||
/** | ||
* El pattern matching se puede utilizar como una función anónima definida dentro de un bloque de código. | ||
*/ | ||
val listaTransformada = listaDeNúmeros.map { | ||
case v if v % 2 == 0 => s"$v es impar" | ||
case 1 => "el primero" | ||
case _ => "otro número" | ||
} | ||
println(listaTransformada) | ||
|
||
val listaTransformadaConNotaciónExplicita = listaDeNúmeros.map { //Aquí está el pattern matching | ||
case v if v % 2 == 0 => s"$v es impar" | ||
case 1 => "el primero" | ||
case _ => "otro número" | ||
} | ||
println(listaTransformadaConNotaciónExplicita) | ||
println( | ||
s"Las listas transformadas son iguales? ${listaTransformada.equals(listaTransformadaConNotaciónExplicita)}" | ||
) | ||
|
||
} |
97 changes: 97 additions & 0 deletions
97
src/main/scala/lecciones/parte4Patrones/PatternMatching.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
|
||
package lecciones.parte4Patrones | ||
|
||
import scala.util.Random | ||
|
||
/** | ||
* Lección 1 de 4. | ||
* | ||
* El pattern matching es usado de forma intensiva por los desarrollados Scala profesionales. | ||
* @note La validación se realiza de forma secuencial. Podemos establecer un orden preferencial. | ||
* @see En la sección [[PatronesDondeNoLosEsperas]] veremos como Scala lo usa sin que nosotros nos demos cuenta. | ||
*/ | ||
object PatternMatching extends App { | ||
|
||
/** | ||
* El pattern matching funciona como un switch en esteroides. | ||
*/ | ||
val generadorAleatorio: Random = new Random() | ||
val númeroAleatorio = generadorAleatorio.nextInt(10) | ||
|
||
val descripción = númeroAleatorio match { | ||
case 1 => "el primero" | ||
case 2 => "doble o nada" | ||
case 3 => "a la tercera va la vencida" | ||
case _ => "otro número" // _ representa cualquier otro caso no contemplado de forma específica | ||
} | ||
|
||
println(númeroAleatorio) | ||
println(descripción) | ||
|
||
/** | ||
* Cuando usamos pattern matching con case classes, Scala hace uso del método unapply que genera para nosotros, | ||
* sin que nos demos cuenta, en la case class. Esto permite hacer descomposición. | ||
*/ | ||
|
||
case class Persona(nombre: String, edad: Int) | ||
val roberto: Persona = Persona("Roberto", 20) | ||
|
||
def saludo(p: Persona) = p match { | ||
case Persona(nombre, edad) if edad < 18 => s"Hola, me llamo $nombre y tengo $edad años. No puedo conducir coches." | ||
case Persona(nombre, edad) => s"Hola, me llamo $nombre y tengo $edad años" | ||
case null => "No sé quien soy" | ||
} | ||
|
||
def saludo_mal(p: Persona) = p match { | ||
// Si escribimos primero el caso general, nunca llegaremos al caso particular | ||
case Persona(n, a) => s"Hola, me llamo $n y tengo $a años" | ||
// Este es el caso particular. Solo se cumple en el caso de que el if sea true | ||
// El compilador nos avisa de que este caso es "unreachable" | ||
case Persona(n, a) if a < 21 => s"Hola, me llamo $n y tengo $a años y no puedo emborracharme en EEUU - USA" | ||
case null => "No sé quien soy" | ||
} | ||
|
||
println(saludo(roberto)) | ||
println(saludo(null)) | ||
println(saludo_mal(roberto)) | ||
|
||
/** | ||
* El pattern matching también se utiliza en jerarquías de clases. | ||
* Esto permite definir métodos que reciben instancias de un tipo elevado en la jerarquía y que serán evaluadas | ||
* a un valor diferente en función del subtipo. | ||
*/ | ||
sealed class Animal | ||
|
||
case class Perro(raza: String) extends Animal | ||
|
||
case class Loro(saludo: String) extends Animal | ||
|
||
val animal: Animal = Perro("Terra Nova") | ||
|
||
def tipoAnimalMensaje(animal: Animal): Unit = animal match { | ||
case Perro(r) => println(s"Este animal es un perro de la raza $r") | ||
// Si utilizas es tipo de patrón y comentas uno de los casos, el compilador te advertirá de que no es exhaustivo | ||
case Loro(s) => println(s"Este animal es un loro que te dice: $s") | ||
} | ||
|
||
println(tipoAnimalMensaje(animal)) | ||
|
||
/** | ||
* Aviso a navegantes | ||
* Aunque el pattern matching es muy poderoso, hay que tener cuidado de cómo lo usamos. | ||
*/ | ||
|
||
// Esta implementación devuelve el resultado que queremos, pero es muy ineficiente | ||
val númeroImpar_pm: Boolean = númeroAleatorio match { | ||
case n if n % 2 == 0 => true | ||
case _ => false | ||
} | ||
|
||
// Podemos implementarlo con if else, pero se puede mejorar | ||
val númeroPar_condicional: Boolean = if (númeroAleatorio % 2 == 0) true else false | ||
|
||
// Esta es la implementación correcta en Scala | ||
val númeroPar_bien: Boolean = númeroAleatorio % 2 == 0 | ||
// "númeroAleatorio % 2 == 0" es una expresión que devuelve un valor de tipo Boolean | ||
|
||
} |
Oops, something went wrong.