Rangos
Una expresión de rango consiste en una sucesión de valores entre un límite inferior y un límite superior; se trata de un intervalo de un conjunto finito definido entre un valor de inicio y un valor final.
En esta entrada vamos a repasar algunas fórmulas para crear distintos tipos de rangos y para recorrer y verificar sus elementos, y veremos algunos atributos y operaciones aplicables a los rangos.
Los rangos son aplicables a cualquier tipo comparable, pero se implementa habitualmente de forma optimizada con algunos tipos primitivos como Int, Long y Char porque los rangos de estos tipos, y específicamente de números enteros, tienen una característica adicional: pueden ser iterados como si fueran un bucle indexado. Por eso en muchas ocasiones se utilizan asociados a estructuras de repetición o bucles.
En Kotlin los rangos son inclusivos por defecto, esto es, incluyen los valores mínimo y máximo. Y la distancia (paso o step) entre dos valores está definida por defecto en 1.
Básicamente en Kotlin existen tres maneras de crear rangos:
1. Con la función rangeTo():
for (i in 1.rangeTo(5)) print(i) // 123452. Con el operador .. que es la forma más común:
val unoAcinco = 1..5 for (n in unoAcinco) print(n) // 12345 for (i in 1..5) print(i) // 12345 for (i in 5..1) print(i) // no imprime nada3. Con la función downTo() que itera en orden inverso o descendente:
for (i in 5.downTo(1)) print(i) // 54321 for (i in 5 downTo 1) print(i) // 54321Otra forma de conseguir el mismo resultado:
for (i in (1..5).reversed()) print(i) // 54321
También se puede crear un rango siguiendo un paso arbitrario distinto de 1 (por defecto) con la función step():
for (i in 1..5 step 2) print(i) // 135 for (i in 5 downTo 1 step 2) print(i) // 531 // obtiene los números pares desde 100 hasta 1 val cienAUno = 100 downTo 1 for (i in cienAUno step 2) { println(i) }
Para crear un rango que no incluya su elemento final, se puede usar la función until:
for (i in 1 until 10) print(i) // 123456789
También podemos crear rangos de caracteres:
val aToZ = "a".."z" for (c in 'a'..'k') print("$c ") // a b c d e f g h i j k for (c in 'k' downTo 'a') print("$c ") // k j i h g f e d c b a
Podemos usar la función forEach() para recorrer el rango de valores:
(1..5).forEach(::print) // 12345 (1..5).reversed().forEach { e -> print("$e ") } // 5 4 3 2 1 (1..9).reversed().forEach { print(it) // 987654321 } (1..9).reversed().step(3).forEach { print(it) // 963 }
Otra opción es usar iterator() para recorrer un rango:
val chars = ('a'..'f') val it = chars.iterator() while (it.hasNext()) { val elemento = it.next() print(elemento) // abcdef }En el código anterior, después de crear un rango de caracteres y un iterador a partir de ese rango, creamos un bucle while para recorrer el rango cuyo cuerpo se ejecuta mientras el método hasNext() verifique si hay un elemento siguiente en el rango, y en ese caso, el método next() recupera ese elemento, que es asignado a la variable.
El operador in (y su opuesto !in) es usado para verificar si un valor está presente en un rango dado:
if (5 in 1..10) print("5 está en el rango") fun esLetraMayuscula (letra:Char) = letra in 'A'..'Z' println(esLetraMayuscula('b')) //false println(esLetraMayuscula('Q')) //true } val x = 4 when (x) { in 1..10 -> print("x está en el rango") !in 10..20 -> print("x no está en el rango") }
Y aunque no podemos usar Strings para crear rangos (necesitamos conjuntos finitos), sí podemos comprobar si un elemento de este tipo está entre dos Strings:
fun estaEntreStrings (cadena: String) = cadena in "Java" .. "Kotlin" println(estaEntreStrings("Javascript")) // true println(estaEntreStrings("Python")) // false
Como conjunto o almacén de valores, los rangos tienen ciertas semejanzas con las colecciones (sobre las que trataremos en entradas posteriores) y comparten con ellas algunas características y operaciones. Por una parte, además del atributo de paso (step), un rango en Kotlin tiene los atributos first y last que devuelven el primer y último valor respectivamente.
println((1..10 step 4).step) // 4 println((1..10 step 3).first) // 1 println((1..10 step 2).last) // 9Y por otra parte, a los rangos también se les puede aplicar ciertas operaciones con los métodos filter, reduce y map:
val nums = (1..15) // filter selecciona los números pares y devuelve una lista con esos valores println(nums.filter { e -> e % 2 == 0 }) // [2, 4, 6, 8, 10, 12, 14] // map aplica la operación a cada elemento y devuelve una lista con los nuevos valores println(nums.map { e -> e * 5 }) // [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75] // reduce aplica la expresión a cada elemento y devuelve un solo valor: println(nums.reduce { total, next -> total + next }) // 120Además existen otras operaciones predefinidas para devolver un valor determinado (min, max, sum, average, count), para ordenar los elementos (sorted) y para crear una lista sin elementos repetidos (distinct):
val r = (1..10) println(r.min()) // 1 println(r.max()) // 10 println(r.sum()) // 55 println(r.average()) // 5.5 println(r.count()) // 10 val s = (1..10 step 2) val t = (r + s) for (i in t) print(i) // 1234567891013579 for (i in t.sorted()) print(i) // 1123345567789910 print(t.distinct()) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Comentarios
Publicar un comentario