Saltar al contenido principal

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.

ListaMapaArray
Se escribe[10, 20, 30]{"vidas": 3, "nivel": 1}{10, 20, 30} o var a[10]
Se lee conla posición: l[0]la clave: m["vidas"]una posición por dimensión: t[1][2]
Tamañocrece y encogecrece y encogefijo
len()sísíno: size()
println enseñael contenidoel contenidosolo 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, usa append() o insert().
  • 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 dan false.
  • + no une listas: lanza ERR_TYPE_MISMATCH. Para unir dos, recorre la segunda con foreach y añade sus elementos a la primera con append().
  • println enseña las cadenas de una lista sin comillas.

Métodos de las listas​

MétodoQué haceDevuelve
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ón

lista.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) da true.
  • 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 a reverse().

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á lanza ERR_KEY_NOT_FOUND. Para leer sin excepción, usa get; para saber si está, contains.
  • {} es un mapa vacío, no un array.
  • No hay mapa.clave: jugador.vidas lanza ERR_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, nil o un objeto. Una lista o un mapa como clave lanza ERR_TYPE_MISMATCH.
  • Un entero y un decimal que valen lo mismo son la misma clave: mapa.get(2.0) encuentra lo que guardaste en mapa[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étodoQué haceDevuelve
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 lanza ERR_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 (lanza ERR_TYPE_MISMATCH): usa size().
  • println de un array no enseña los elementos, solo sus dimensiones. Para verlos, recórrelo con foreach.
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étodoQué haceDevuelve
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:

Recorresx vale
Una listaCada elemento, en orden.
Un arrayCada elemento, fila a fila: todos, aunque tenga varias dimensiones.
Un mapaCada valor, en el orden del mapa.
Una cadenaCada 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, foreach decide 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 lanza ERR_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í mismas

Si 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:

ListaMapa
En el programa principal252 elementos127 pares
Dentro de una función sin parámetros ni variables locales251 elementos126 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.

Si no cabe, la VM se cierra

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.

  • Léela de un fichero con File y conviértela con JSON.

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). Ver remove.
  • 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: usa get o contains. Y no hay mapa.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() y claves[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.