Listas, mapas y arrays
JARU tiene tres formas de guardar muchos valores en una sola variable: las listas, los mapas y los arrays. Las tres admiten valores de cualquier tipo, mezclados. No necesitan use: sus métodos están siempre. Lo general de los tipos de datos está en Tipos de datos.
| Lista | Mapa | Array | |
|---|---|---|---|
| Se escribe | [10, 20, 30] | {"vidas": 3, "nivel": 1} | {10, 20, 30} o var a[10] |
| Se lee con | la posición: l[0] | la clave: m["vidas"] | una posición por dimensión: t[1][2] |
| Tamaño | crece y encoge | crece y encoge | fijo |
len() | sí | sí | no: size() |
println enseña | el contenido | el contenido | solo las dimensiones |
Asignar una lista, un mapa o un array a otra variable, o pasarlo a una función, no lo copia: las dos variables ven el mismo (ver Copiar y clonar).
Las listas, los mapas y los arrays funcionan igual en Windows y en todas las placas.
Listas
Una lista guarda valores en orden. Se escribe entre corchetes, y puede crecer y encoger:
var vacia = []
var puntos = [10, 20, 30]
var mezcla = ["Ana", 25, true, nil, [1, 2]]
println(puntos) // [10, 20, 30]
println(mezcla) // [Ana, 25, true, nil, [1, 2]]
lista[i] lee el elemento de la posición i, y lista[i] = valor lo cambia. La primera posición es la 0, y la última, lista.size() - 1:
var puntos = [10, 20, 30]
println(puntos[0]) // 10
puntos[2] = 35
println(puntos) // [10, 20, 35]
- Una posición negativa o que no existe lanza
ERR_INDEX_OUT_OF_RANGE, también al escribir. Para añadir elementos, usaappend()oinsert(). - La posición tiene que ser un entero. Con un decimal lanza
ERR_TYPE_MISMATCH. - Una lista dentro de otra se lee con dos corchetes:
tabla[1][0]. ==mira si las dos son la misma lista, no si tienen lo mismo: dos listas distintas con los mismos elementos danfalse.+no une listas: lanzaERR_TYPE_MISMATCH. Para unir dos, recorre la segunda conforeachy añade sus elementos a la primera conappend().printlnenseña las cadenas de una lista sin comillas.
Métodos de las listas
| Método | Qué hace | Devuelve |
|---|---|---|
append(valor) | Añade un elemento al final. | nil |
insert(posicion, valor) | Añade un elemento en una posición. | nil |
pop(posicion) | Quita el elemento de esa posición, o el último. | El elemento quitado |
remove(valor) | Quita el primer elemento igual a valor. | true si lo ha quitado |
contains(valor) | Mira si hay un elemento igual a valor. | true o false |
size() | Cuenta los elementos. | Entero |
reverse() | Le da la vuelta a la lista. | true |
sort() | Ordena la lista de menor a mayor. | true |
clear() | Vacía la lista. | nil |
copy() | Hace una copia superficial: ver Copiar y clonar. | Lista nueva |
clone() | Hace una copia profunda: ver Copiar y clonar. | Lista nueva |
Los métodos que cambian la lista la cambian a ella: no devuelven una lista nueva. Con más o menos argumentos de los suyos lanzan ERR_INVALID_ARG_NUMBER.
lista.append(valor)
Añade valor al final de la lista.
var nombres = ["Ana"]
nombres.append("Luis")
println(nombres) // [Ana, Luis]
lista.insert(posicion, valor)
Añade valor en posicion y corre una posición los elementos que había desde ahí. posicion va de 0 a size(): con size(), lo añade al final.
var letras = ["a", "c"]
letras.insert(1, "b")
letras.insert(3, "d")
println(letras) // [a, b, c, d]
Con una posición negativa o mayor que size() lanza ERR_INDEX_OUT_OF_RANGE, y si no es un entero, ERR_TYPE_MISMATCH.
lista.pop(posicion)
Quita el elemento de posicion y lo devuelve. Sin argumento, quita el último.
var cola = [10, 20, 30]
var ultimo = cola.pop()
var primero = cola.pop(0)
println(ultimo, " ", primero, " ", cola) // 30 10 [20]
- Con la lista vacía lanza
ERR_EMPTY_LIST. - Con una posición negativa o que no existe lanza
ERR_INDEX_OUT_OF_RANGE, y si no es un entero,ERR_TYPE_MISMATCH.
lista.remove(valor)
Quita el primer elemento igual a valor y devuelve true. Si no lo encuentra, no cambia nada y devuelve false, sin lanzar ninguna excepción.
var numeros = [5, 1, 7, 1]
println(numeros.remove(1)) // true
println(numeros) // [5, 7, 1]
println(numeros.remove(9)) // false
Dos valores son iguales como en contains.
remove quita por valor, no por posiciónlista.remove(2) no quita el elemento de la posición 2: quita el primer elemento que vale 2, si lo hay. Para quitar por posición, usa pop(posicion). Un bucle que espera a que la lista encoja llamando a remove(i) se queda colgado si ningún elemento vale i.
lista.contains(valor)
Devuelve true si algún elemento es igual a valor.
var inventario = ["espada", "llave"]
println(inventario.contains("llave")) // true
println(inventario.contains("mapa")) // false
- Los números son iguales si valen lo mismo: en
[1, 2],contains(1.0)datrue. - Las cadenas son iguales si tienen el mismo texto.
- Una lista, un mapa o un objeto solo es igual a sí mismo: otra lista con los mismos elementos no cuenta.
lista.size()
Devuelve el número de elementos. Es lo mismo que len(lista).
var puntos = [10, 20, 30]
var vacia = []
println(puntos.size(), " ", vacia.size()) // 3 0
lista.reverse()
Le da la vuelta a la lista.
var cuenta = [1, 2, 3]
cuenta.reverse()
println(cuenta) // [3, 2, 1]
lista.sort()
Ordena la lista de menor a mayor. La lista tiene que ser toda de números (enteros y decimales mezclados valen) o toda de cadenas. Con cualquier otra cosa, también con un nil, lanza ERR_TYPE_MISMATCH.
var puntos = [30, 5.5, -2, 10]
puntos.sort()
println(puntos) // [-2, 5.5, 10, 30]
var nombres = ["pera", "Manzana", "zorro", "árbol"]
nombres.sort()
println(nombres) // [Manzana, pera, zorro, árbol]
- Las cadenas se ordenan por sus bytes: las mayúsculas van antes que las minúsculas, y las letras con tilde y la ñ, detrás de la z. Para ordenar sin mirar las mayúsculas, pasa antes las cadenas a minúsculas con
lower(). - Para ordenar de mayor a menor, llama a
sort()y después areverse().
lista.clear()
Quita todos los elementos.
var cola = [1, 2, 3]
cola.clear()
println(cola, " ", cola.size()) // [] 0
Mapas
Un mapa guarda pares de clave y valor: buscas el valor por su clave. Se escribe entre llaves, con dos puntos entre la clave y el valor. mapa[clave] lee un valor, y mapa[clave] = valor lo cambia o, si la clave no está, la añade:
var jugador = {"nombre": "Ana", "vidas": 3}
println(jugador["nombre"]) // Ana
jugador["vidas"] = 2
jugador["nivel"] = 1
println(jugador.size()) // 3
mapa[clave]con una clave que no está lanzaERR_KEY_NOT_FOUND. Para leer sin excepción, usaget; para saber si está,contains.{}es un mapa vacío, no un array.- No hay
mapa.clave:jugador.vidaslanzaERR_ATTRIBUTE_NOT_FOUND. Si quieres escribir así, usa un struct o una clase. - Como en las listas,
==mira si es el mismo mapa, no si tiene lo mismo.
Las claves
- Una clave puede ser cualquier valor menos una lista o un mapa: una cadena, un número,
true,false,nilo un objeto. Una lista o un mapa como clave lanzaERR_TYPE_MISMATCH. - Un entero y un decimal que valen lo mismo son la misma clave:
mapa.get(2.0)encuentra lo que guardaste enmapa[2]. - Con un objeto como clave cuenta el objeto, no lo que tiene dentro: otro objeto de la misma clase es otra clave.
- Cada clave está una sola vez: escribir en una clave que ya está cambia su valor.
var nombres = {1: "uno", 2: "dos"}
nombres[2] = "DOS"
println(nombres[2], " ", nombres.size()) // DOS 2
Con JSON.stringify, las claves enteras se escriben como texto: {1: "uno"} sale {"1": "uno"}, y al leerlo con JSON.parse la clave es la cadena "1". Ver JSON.
El orden
Un mapa no guarda el orden en que metiste las claves. keys(), values(), items(), foreach y println lo recorren en el orden de su tabla interna, que puede cambiar al añadir claves:
var datos = {"vidas": 3, "nivel": 1}
println(datos) // {nivel: 1, vidas: 3}
Si necesitas un orden, ordena las claves: var claves = mapa.keys() y después claves.sort().
Métodos de los mapas
| Método | Qué hace | Devuelve |
|---|---|---|
insert(clave, valor) | Añade una clave, o cambia su valor. | nil |
get(clave, defecto) | Lee un valor sin lanzar excepciones. | El valor, o defecto |
contains(clave) | Mira si la clave está. | true o false |
remove(clave) | Quita una clave. | true si estaba |
pop(clave, defecto) | Quita una clave y devuelve su valor. | El valor, o defecto |
size() | Cuenta las claves. | Entero |
keys() | Da las claves. | Lista |
values() | Da los valores. | Lista |
items() | Da los pares. | Lista de listas [clave, valor] |
clear() | Vacía el mapa. | nil |
copy() | Hace una copia superficial: ver Copiar y clonar. | Mapa nuevo |
clone() | Hace una copia profunda: ver Copiar y clonar. | Mapa nuevo |
Con más o menos argumentos de los suyos lanzan ERR_INVALID_ARG_NUMBER, y con una clave que no puede serlo (una lista, un mapa), ERR_TYPE_MISMATCH.
mapa.insert(clave, valor)
Añade clave con su valor o, si ya estaba, cambia su valor. Es lo mismo que mapa[clave] = valor.
var ajustes = {}
ajustes.insert("volumen", 8)
ajustes["brillo"] = 5
println(ajustes.size()) // 2
mapa.get(clave, defecto)
Devuelve el valor de clave. Si la clave no está, no lanza ninguna excepción: devuelve defecto, o nil si no lo pones.
var ajustes = {"volumen": 8}
println(ajustes.get("volumen")) // 8
println(ajustes.get("brillo")) // nil
println(ajustes.get("brillo", 5)) // 5
mapa.contains(clave)
Devuelve true si la clave está en el mapa. Mira las claves, no los valores.
var ajustes = {"volumen": 8}
println(ajustes.contains("volumen")) // true
println(ajustes.contains(8)) // false
mapa.remove(clave)
Quita la clave y su valor, y devuelve true. Si la clave no estaba, devuelve false, sin lanzar ninguna excepción.
var ajustes = {"volumen": 8, "brillo": 5}
println(ajustes.remove("brillo")) // true
println(ajustes.remove("brillo")) // false
println(ajustes) // {volumen: 8}
mapa.pop(clave, defecto)
Quita la clave y devuelve su valor. Si la clave no está, devuelve defecto; sin defecto, lanza ERR_KEY_NOT_FOUND.
var ajustes = {"volumen": 8}
var v = ajustes.pop("volumen")
println(v, " ", ajustes) // 8 {}
println(ajustes.pop("brillo", 0)) // 0
mapa.size()
Devuelve el número de claves. Es lo mismo que len(mapa).
var ajustes = {"volumen": 8, "brillo": 5}
println(ajustes.size()) // 2
mapa.keys()
Devuelve una lista nueva con las claves, en el orden del mapa (ver El orden).
var precios = {"pan": 1, "leche": 2}
println(precios.keys()) // [pan, leche]
mapa.values()
Devuelve una lista nueva con los valores, en el mismo orden que keys().
var precios = {"pan": 1, "leche": 2}
println(precios.values()) // [1, 2]
mapa.items()
Devuelve una lista nueva con un elemento por clave: una lista de dos, [clave, valor], en el mismo orden que keys().
var precios = {"pan": 1, "leche": 2}
println(precios.items()) // [[pan, 1], [leche, 2]]
mapa.clear()
Quita todas las claves.
var ajustes = {"volumen": 8}
ajustes.clear()
println(ajustes, " ", ajustes.size()) // {} 0
Arrays
Un array tiene un tamaño fijo, que se decide al crearlo, y puede tener varias dimensiones. Es lo que conviene para una tabla de tamaño conocido, como un tablero o una rejilla. Se crea de dos formas:
- Con sus elementos entre llaves. Para varias dimensiones, unas llaves dentro de otras.
- Con
var nombre[tamaño], una dimensión por corchete. Todos los elementos empiezan valiendo 0, y el tamaño puede ser una variable.
var primos = {2, 3, 5, 7}
var tablero = {{1, 2, 3}, {4, 5, 6}}
var rejilla[20][15]
var n = 8
var fila[n]
println(primos[2], " ", tablero[1][0], " ", rejilla[19][14], " ", fila.size()) // 5 4 0 8
- Se lee y se escribe con una posición por dimensión, cada una desde 0:
tablero[1][2] = 9. Una posición negativa o fuera de su dimensión lanzaERR_INDEX_OUT_OF_RANGE, y una que no es un entero,ERR_TYPE_MISMATCH. {}vacío es un mapa: un array escrito entre llaves necesita al menos un elemento.len()no admite arrays (lanzaERR_TYPE_MISMATCH): usasize().printlnde un array no enseña los elementos, solo sus dimensiones. Para verlos, recórrelo conforeach.
var tablero = {{1, 2, 3}, {4, 5, 6}}
println(tablero) // Array <2*3*>
Todas las filas de un array escrito entre llaves tienen que tener el mismo número de elementos: si no, el compilador da el error «All the rows of an array literal must have the same number of elements.».
var tablero = {{1, 2, 3}, {4, 5}}
Una posición sola en un array de varias dimensiones
Un array de varias dimensiones no tiene filas que se puedan sacar: es una tabla de elementos seguidos, fila a fila. Con una sola posición, lees el elemento de ese número en ese orden. En un array de 2 x 3, tablero[5] es el último elemento, y tablero[1] es el segundo de la primera fila, no la segunda fila:
var tablero = {{1, 2, 3}, {4, 5, 6}}
println(tablero[1][2]) // 6
println(tablero[5]) // 6
println(tablero[1]) // 2
Listas dentro de un array
Un array puede guardar listas, pero grupos[0][1] no lee el segundo elemento de la lista: el array toma las dos posiciones como suyas y lanza ERR_INDEX_OUT_OF_RANGE. Pon el array y su posición entre paréntesis, o guarda antes la lista en una variable:
var grupos = {["Ana", "Luis"], ["Marta"]}
println((grupos[0])[1]) // Luis
var primero = grupos[0]
println(primero[1]) // Luis
Un array dentro de una lista o de un mapa sí se lee con los corchetes seguidos: niveles[1][2].
Métodos de los arrays
| Método | Qué hace | Devuelve |
|---|---|---|
size() | Cuenta todos los elementos. | Entero |
numdim() | Cuenta las dimensiones. | Entero |
dim() | Da el tamaño de cada dimensión. | Lista de enteros |
fill(valor) | Pone el mismo valor en todos los elementos. | nil |
copy() | Hace una copia superficial: ver Copiar y clonar. | Array nuevo |
clone() | Hace una copia profunda: ver Copiar y clonar. | Array nuevo |
array(valor) | Devuelve el mismo array. | El mismo array |
array.size()
Devuelve el número total de elementos: en un array de 3 x 4, 12.
array.numdim()
Devuelve el número de dimensiones: 1 en {1, 2, 3}, 2 en var t[3][4].
array.dim()
Devuelve una lista con el tamaño de cada dimensión.
var t[3][4]
println(t.size(), " ", t.numdim(), " ", t.dim()) // 12 2 [3, 4]
array.fill(valor)
Pone valor en todos los elementos del array.
var t[2][2]
t.fill(-1)
println(t[0][0], " ", t[1][1]) // -1 -1
array.array(valor)
Devuelve el mismo array, sin copiarlo: a.array(0) == a da true. El argumento es obligatorio (sin él lanza ERR_INVALID_ARG_NUMBER), pero no se usa. No crea un array nuevo: para eso, var a[n], y para copiarlo, copy() o clone().
Recorrerlos con foreach
foreach (var x in coleccion) recorre una colección y da un valor en cada vuelta (la sintaxis está en Estructuras de control). Lo que vale x depende de lo que recorres:
| Recorres | x vale |
|---|---|
| Una lista | Cada elemento, en orden. |
| Un array | Cada elemento, fila a fila: todos, aunque tenga varias dimensiones. |
| Un mapa | Cada valor, en el orden del mapa. |
| Una cadena | Cada carácter (ver Cadenas). |
Con cualquier otra cosa (un número, nil, un struct) lanza ERR_TYPE_MISMATCH.
Para recorrer las claves de un mapa, recorre mapa.keys(), y para las claves y los valores, mapa.items():
var precios = {"pan": 1, "leche": 2}
foreach (var p in precios)
print(p, " ")
end
println("")
foreach (var par in precios.items())
println(par[0], ": ", par[1])
end
1 2
pan: 1
leche: 2
Asignar a la variable del bucle
Asignar a la variable del bucle cambia el elemento que estás recorriendo. En un mapa, cambia el valor de la entrada actual (desde la 0.9.7).
var precios = {"pan": 1, "leche": 2}
foreach (var p in precios)
p = p * 10
end
println(precios) // {pan: 10, leche: 20}
var lista = [1, 2, 3]
foreach (var x in lista)
x = x * 2
end
println(lista) // [2, 4, 6]
En una cadena, asignar a la variable cambia ese carácter: la variable que tiene la cadena pasa a tener una cadena nueva, como con texto[i] = "x".
Cambiar la colección mientras la recorres
- En una lista,
foreachdecide al empezar cuántas vueltas da. Lo que añadas dentro del bucle no se recorre. Si quitas elementos, se salta alguno, y al final lanzaERR_INDEX_OUT_OF_RANGE. - En un mapa, añadir claves dentro del bucle puede hacer que se salte entradas o que recorra alguna de las nuevas.
Para quitar o añadir mientras recorres, recorre una copia (lista.copy(), mapa.keys()) y cambia el original:
var enemigos = [3, 0, 5, 0]
foreach (var e in enemigos.copy())
if (e == 0) then
enemigos.remove(e)
end
end
println(enemigos) // [3, 5]
Copiar y clonar
Asignar no copia: después de b = a, las dos variables ven la misma lista. Pasa lo mismo al pasarla a una función (ver Funciones).
var a = [1, 2]
var b = a
b.append(3)
println(a) // [1, 2, 3]
Para tener otra, cópiala. Las listas, los mapas y los arrays tienen dos métodos:
copy()hace una copia superficial: un contenedor nuevo con los mismos elementos. Si un elemento es otra lista, un mapa o un array, la copia y el original comparten ese de dentro.clone()hace una copia profunda: copia también las listas, los mapas, los arrays, los structs y los objetos que haya dentro, a cualquier profundidad. Lo demás (bitmaps, sprites, ficheros…) no se copia: el clon usa el mismo.
var original = [1, [2, 3]]
var copia = original.copy()
var clon = original.clone()
var interior = original[1]
interior.append(4)
original[0] = 100
println(original) // [100, [2, 3, 4]]
println(copia) // [1, [2, 3, 4]]
println(clon) // [1, [2, 3]]
Los structs y los objetos también tienen copy() y clone(). Las cadenas no hace falta copiarlas, porque no cambian nunca.
clone() con colecciones que se contienen a sí mismasSi dentro de lo que clonas hay un ciclo, como una lista que se tiene a sí misma dentro o dos mapas que se apuntan entre sí, clone() no termina nunca de copiar: la VM se cierra sin ningún mensaje, y try no lo evita. Con ciclos, usa copy() o copia a mano lo que necesites.
Límites
Listas y mapas escritos de una vez
Una lista o un mapa escrito entre corchetes o llaves se monta en la pila de la VM, que tiene sitio para 256 valores en todas las plataformas. En el programa principal caben:
| Lista | Mapa | |
|---|---|---|
| En el programa principal | 252 elementos | 127 pares |
| Dentro de una función sin parámetros ni variables locales | 251 elementos | 126 pares |
Cada llamada en curso y cada variable local que haya en ese momento (también los parámetros y las variables de un foreach) ocupan un sitio más, y caben menos: en una función con un parámetro y dos variables locales, 248 elementos. Cada par de un mapa ocupa dos sitios.
Con una lista o un mapa escrito de una vez que no cabe, la VM se cierra de golpe, sin un mensaje de JARU que diga por qué, también en las placas, y try no lo captura. Si tu lista pasa de unos 200 elementos, móntala de otra forma.
Formas de montar una lista grande:
-
Escríbela en trozos y únelos con
append():var tabla = [0, 1, 2, 3]
var resto = [4, 5, 6, 7]
foreach (var v in resto)
tabla.append(v)
end
println(tabla.size()) // 8 -
Si su tamaño no va a cambiar, usa un array: un array escrito entre llaves no tiene este límite, porque sus elementos se guardan de uno en uno.
Memoria
Aparte de eso, el tamaño de una lista, un mapa o un array solo lo limita la memoria. En las placas hay mucha menos que en Windows: ver Memoria y límites.
Ejemplo completo
Este programa apunta las jugadas de un tres en raya en un array de 3 x 3, cuenta las de cada jugador en un mapa y pinta el tablero con una lista por fila.
var tablero[3][3]
tablero.fill(".")
var jugadas = [[0, 0, "X"], [1, 1, "O"], [2, 2, "X"], [0, 2, "O"]]
var cuenta = {"X": 0, "O": 0}
foreach (var j in jugadas)
tablero[j[0]][j[1]] = j[2]
cuenta[j[2]] = cuenta[j[2]] + 1
end
var espacio = " "
for (var f = 0; f < 3; f++)
var fila = []
for (var c = 0; c < 3; c++)
fila.append(tablero[f][c])
end
println(espacio.join(fila))
end
var jugadores = cuenta.keys()
jugadores.sort()
foreach (var nombre in jugadores)
println(nombre, ": ", cuenta[nombre], " jugadas")
end
X . O
. O .
. . X
O: 2 jugadas
X: 2 jugadas
Trampas y problemas conocidos
remove(valor)quita por valor, no por posición: para quitar una posición,pop(posicion). Verremove.- Asignar no copia, ni pasar a una función: ver Copiar y clonar.
==no compara el contenido de dos listas o dos mapas: mira si son el mismo.- Un mapa no guarda el orden en que metiste las claves: ver El orden.
mapa[clave]con una clave que no está lanza una excepción: usagetocontains. Y no haymapa.clave.{}es un mapa, no un array.- En un array de varias dimensiones, una sola posición cuenta los elementos seguidos, y una lista dentro de un array se lee con paréntesis: ver Arrays.
- No indexes lo que devuelve un método:
mapa.keys()[0]no funciona, y puede incluso cerrar el compilador. Guarda antes el resultado en una variable:var claves = mapa.keys()yclaves[0]. Más en Sintaxis básica. - Cambiar una colección mientras la recorres se salta elementos: ver Recorrerlos con foreach.
- Una lista o un mapa escrito de una vez tiene un tamaño máximo, y pasarse cierra la VM: ver Límites.
Todos los problemas conocidos de JARU, juntos, están en Problemas conocidos.