Comprender las opciones en Scala
En este artículo, aprenderemos cómo trabajar con elementos de datos opcionales en Scala. Las Option
en Scala nos ayudan a escribir código robusto.
el tipo Option
en Scala
Option[T]
en un Scala es un contenedor
que almacena cero o un valor de un tipo dado. Manejando valores opcionales usando la Option
de Scala, podemos asegurar dos cosas:
- Tipo seguridad: Podemos parametrizar los valores opcionales.
- Conciencia funcional: el tipo
Option
proporciona muchas capacidades funcionales que aseguran que se creen menos errores en el programa.
En Scala, una Option[T]
está representada por el objeto Some[T]
o None[T]
. Aquí scala.Option
es la clase base que es abstracta y extendida, haciendo que la Option
en Scala se comporte como un contenedor.
Recuerda que la clase Option
y sus subclases requieren un tipo concreto. Pueden ser explícitos o inferidos como la siguiente sintaxis.
Ejemplo:
val obj1: Option[Int] = None
val obj2 = Some(100)
Aquí obj1
y obj2
son objetos de Option[Int]
. Una pregunta común es cómo comprobar si nuestra Option
es Some
o None
.
Para resolver este problema, tenemos las siguientes funciones.
isEmpty
: este método devuelvetrue
si el objeto esNone
.nonEmpty
: este método devuelvetrue
si el objeto esSome
.isDefined
: este método devuelvetrue
si el objeto esSome
.
Recuperar el Valor de la Option
en Scala
Podemos usar el método get
para recuperar el valor de Option
. Si se invoca el método get
en un objeto de None
, entonces se lanza NoSuchElementException
, también conocido como sesgo de éxito
.
Código de ejemplo:
val obj1: Option[Int]= ...
val v1 = if (obj1.isDefined)
{
obj1.get
}
else
{
0
}
El código anterior también se puede escribir utilizando la coincidencia de patrones.
Código de ejemplo:
val obj1: Option[Int]= ...
val v1 = obj1 match {
case Some(temp) =>
temp
case None =>
0
}
También tenemos otros métodos como getOrElse
y orElse
para recuperar valores.
getOrElse
: Este método recupera valores si el objeto esSome
; de lo contrario, devuelve un valor predeterminado.orElse
: Este método recupera valores si el objeto esSome
; de lo contrario, devuelve una opciónalternate
.
Cuando el valor opcional no está establecido, el método getOrElse
es muy útil ya que podemos devolver un valor predeterminado.
Sintaxis:
val v1 = obj1.getOrElse(0)
la Option
como Contenedor en Scala
Como hemos visto que Option
es un contenedor en Scala para otro valor, así como atravesamos una List
de manera similar, podemos atravesar una Option
.
Veamos algunos métodos para este contenedor. Sintaxis del método Option.map
en Scala:
final def map[Y](f: (X) => Y): Option[Y]
Código de ejemplo:
val obj1: Option[Int] = Some(100)
assert(obj1.map(_.toString).contains("100"))
assert(obj1.map(_ * 2.0).contains(200))
val obj2: Option[Int] = None
assert(obj2.map(_.toString).isEmpty)
En el ejemplo anterior, usamos el método Option.map
para convertir los valores contenidos a otro tipo. El método Option.map
también podría usarse para afectar el control de flujo
del programa.
Código de ejemplo:
val obj1: Option[Int] = Some(10)
val obj2: Option[Int] = None
def mul(n: Int): Int = n * 2
assert(obj1.map(mul).contains(20))
assert(obj2.map(mul).isEmpty)
Aquí creamos dos objetos de Opción[Int]
, el primer objeto tiene un valor de 10
, y el segundo está vacío. Luego definimos un método mul
que multiplica la entrada por 2
.
Finalmente, mapeamos obj1
y obj2
utilizando el método mul
. Mapear obj1 nos da Some(20)
, y mapear obj2 nos da None
.
Una cosa a observar aquí es que en la segunda llamada, obj2.map(mul)
, el método mul
no se llama en absoluto. También podemos controlar el flujo con filtrado.
Tenemos muchos métodos para hacerlo:
filtro
: este método devuelveOption
si el valor deOption
esSome
y la función proporcionada devuelvetrue
.existe
: este método devuelvetrue
si laOption
está configurada y la función suministrada devuelvetrue
.forall
: Se comporta igual que el métodoexists
.
Usando estas funciones, podemos escribir el código de manera muy concisa.
Pongamos un ejemplo en el que encontremos el equipo con la puntuación más alta.
Código de ejemplo:
def highScoringTeam(playerA: Player, playerB: Player, tournament: Tournament): Option[(Player, Int)] =
{ getTopScore(playerA, tournament).foldRight(getTopScore(playerB, tournament)) {
case (playerAInfo, playerBInfo) => playerBInfo.filter {
case (_, scoreB) => scoreB > playerAInfo._2
}.orElse(Some(playerAInfo))
}
}
En el código anterior, hemos aprovechado que las Option
se pueden usar como colecciones. La única diferencia es que una opción puede tener como máximo un valor.