viernes, 3 de febrero de 2017

Un resumen de Scala for the Impatient, parte 8


Arrays de longitud fija

Si necesitamos usar un array de longitud fija, puede utilizar una estructura Array, veamos un ejemplo:

val nums = new Array[Int](10)
// Se crea un arreglo de 10 elementos de tipo entero inicializados en 0

val a = new Array[String](10)
// un arreglo de string inicializados en null

val s = Array("Hello", "World")
// Un arreglo de 2 strings

s(0) = "Goodbye"
// Array("Goodbye", "World")
// Se puede usar () en vez de []

Arreglos de longitud variable

Java tiene ArrayList y C++ el vector de la STL, equivalente en scala es el ArrayBuffer.

import scala.collection.mutable.ArrayBuffer

val b = ArrayBuffer[Int]()
// o new ArrayBuffer[Int]
// ahora tenemos un array de 0 elementos (vacio)

b += 1
// ArrayBuffer(1)
// Agregamos un elemento al final con +=

b += (1, 2, 3, 5)
// ArrayBuffer(1, 1, 2, 3, 5)
// agregamos multiples elementos al final con += y los elementos entre parentesis

b ++= Array(8, 13, 21)
// ArrayBuffer(1, 1, 2, 3, 5, 8, 13, 21)
// Se puede agregar colecciones con el operador ++=

b.trimEnd(5)
// ArrayBuffer(1, 1, 2)
// remueve los últimos 5 elementos del final

Se pueden insertar elementos en cualquier parte del vector con insert:

b.insert(2, 6)
// ArrayBuffer(1, 1, 6, 2)
// inserta en la posición 3 es decir en el índice 2 (recuerden que el índice comienza en 0)

b.insert(2, 7, 8, 9)
// ArrayBuffer(1, 1, 7, 8, 9, 6, 2)
// se pueden insertar multiples elementos

b.remove(2)
// ArrayBuffer(1, 1, 8, 9, 6, 2)

b.remove(2, 3)
// ArrayBuffer(1, 1, 2)
// El segundo parámetro nos indica cuantos elementos borrar.

En ciertas ocasiones debemos utilizar un array y no un ArrayBuffer por lo que podemos convertir estos tipos con las funciones toArray y toBuffer

En c++ y Java existen muchas diferencias entre la sintaxis de un array y una lista o vector dinámico,   en scala esto es mucho más uniforme.

Por ejemplo aquí podemos ver como recorrer un Array o un ArrayBuffer:

 for (i <- 0 until a.length)
   println(i + ": " + a(i))

La variable i va ir desde 0 hasta a.length – 1

until es un método de RichInt que retorna el rango desde el elemento hasta el parámetro -1 (es decir sin incluir este elemento)

0 until 10
// Range(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

Podemos llamar a este método con 0.until(10)

Por lo tanto podemos deducir que el for trabaja de la siguiente manera:

for (i <- rango)

y por cada iteración i toma un valor del rango. until guarda más secretos, podemos hacer rangos con  saltos:

0 until (a.length, 2)
// Range(0, 2, 4, ...)

o podemos hacer rangos y luego darlos vuelta:

(0 until a.length).reverse
// Range(..., 2, 1, 0)

Si no necesitamos índices para recorrer el arreglo, solo podemos asignar los elementos de la colección:

for (elem <- a)
println(elem)

Como se puede ver es similar a iterar con un rango.

Transformar un arreglo

Podemos transformar arreglos de forma similar que Java o C++ pero tenemos una herramienta mucho más simple. Podemos transformar los arreglos por comprensión:

val a = Array(2, 3, 5, 7, 11)
val result = for (elem <- a) yield 2 * elem
// result is Array(4, 6, 10, 14, 22

yield es una palabra reservada, este operador permite retornar otro arreglo con los elementos modificados según la operación que se aplique. En el ejemplo result contiene el arreglo resultado.

También podemos filtrar con el for y luego aplicar la transformación:

for (elem <- a if a % 2 == 0) yield 2 * elem

Esto no afecta la colección original.
Noten que también podríamos usar funciones de map que son más potentes:

a.filter(_ % 2 == 0).map(2 * _)

o también

a filter { _ % 2 == 0 } map { 2 * _ }

Algoritmos comunes 

Similar a la STL de c++ o a las clases Collections y Arrays de java. Scala cuenta con métodos que nos permiten acumular o ordenar:

 Array(1, 7, 2, 9).sum
// 19
// Esto también funciona con ArrayBuffer

Note que sum funciona para valores de tipo numéricos, que se pueden sumar. De igual manera min y max funciona para valores que se pueden comparar.

ArrayBuffer("Mary", "had", "a", "little", "lamb").max
// "little"

El método para ordenar retorna una colección ordenada, sin modificar la colección original:

val b = ArrayBuffer(1, 7, 2, 9)
val bSorted = b.sorted(_ < _)
// b is unchanged; bSorted is ArrayBuffer(1, 2, 7, 9)

Para los arreglos podemos utilizar mkString con el cual podemos hacer strings a partir de nuestros arreglos:

a.mkString(" and ")
// "1 and 2 and 7 and 9"
a.mkString("<", ",", ">")
// "<1,2,7,9>"

Arreglos multidimensionales.

Similar a Java, scala implementa arreglos multidimensionales como arreglos de arreglos. Por ejemplo:

val matrix = Array.ofDim[Double](3, 4)

y para acceder a los valores es necesario poner paracentesis con el índice de la fila y luego paréntesis con el índice de la columna.

matrix(row)(column) = 42