Cadenas y colecciones

Genie ofrece varias maneras de operar con las cadenas de texto.

También ofrece la posibilidad de agrupar datos del mismo tipo (cadenas, números y cualquier tipo de dato) en una secuencia ordenada llamada array.

Además, Genie tiene otros formatos de datos más sofisticados, a modo de colecciones de elementos, que incluyen las listas y los diccionarios.

Cadenas

En Tipo de datos ya vimos que las cadenas de texto son un tipo de datos que se definen con :string.

Unir cadenas

Sabemos que podemos unir dos cadenas simplemente usando el símbolo “+”.

init
	a: string = "Hola"
	b: string = "Adiós"
	c: string = "Pepe"  
	saludo: string = a + " " + c 
	print saludo
	despedida: string = b + " " + c 
	stdout.printf (despedida)

Otra forma de unir cadenas es utilizando el método concat.

init 
	a:string = "Hola" 
	b:string = "Pepe"  
	saludo:string = ""
	print saludo.concat (a, " ", b)

Conocer longitud

Podemos saber la longitud de una cadena (¡ojo!, los espacios en blanco también cuentan).

init
	a:string = "Genie es mi lenguaje." 
	print ("La frase tiene %d elementos", a.length)

Esto devuelve:

La frase tiene 21 elementos

Chequear contenido

Podemos determinar si una cadena contiene otra, por ejemplo:

init
	contactos:string = "Alicia Pepe Antonio Juan"
 
	if ("Juan" in contactos)
		print "Tengo a Juan en mi agenda"
	else 
		print "No puedo llamarlo."

Y también con contains:

init
	contactos:string = "Alicia Pepe Antonio Juan"
	if contactos.contains ("Pepe")
		print "Tengo a Pepe en la agenda."

Índices

De una cadena podemos extraer cualquier carácter utilizando los corchetes y la posición que ocupa ese carácter en la cadena (otro ¡ojo!, se empieza a contar desde 0).

init
	a:string = "Genie es mi lenguaje."
	print ("El cuarto caracter es %c", a[3])
	print ("El primer caracter es %c", a[0])

Este código devuelve:

El cuarto caracter es i
El primer caracter es G
init
	a:string = "Genie es mi lenguaje."
	print ("%s me encanta.", a[0:5])
Genie me encanta.

También podemos extraer el índice que ocupa un carácter:

init
	a:string = "Genie es mi lenguaje"
	indice:int = a.index_of("G")
	print "%d", indice

Extraer y sustituir

Para sustituir una subcadena por otra con replace:

init
	a:string = "Genie es mi lenguaje"
	print (a.replace("Genie","Python"))

También podemos extraer y sustituir subcadenas con slice y splice:

init
	a:string = "Genie es mi lenguaje"
	// extrae una parte
	extrae:string = a.slice(0, 5)
	print extrae
 
	// extrae y sustituye
	sustituye:string = a.splice(0, 5, "Python")
	print sustituye

Y con los índices que vimos antes:

init
	a:string = "Genie Python"
	b:string = "es mi lenguaje"
	c:string = a[0:6]  // Tomamos también el espacio en blanco
	print c + b

Eso mismo lo podemos hacer con el método substring:

init
	a:string = "Genie Python"
	extracto:string = a.substring (0, 5)
	print extracto

Dividir y unir

También podemos separar una cadena dados ciertos separadores, y unir ciertos elementos en una cadena nueva.

init
	s:string = "Utilizando separadores para dividir cadenas"	
	for s2 in s.split(" ")
		print(s2)
	x:string = "Separa-esta-cadena-por-los-guiones"
	for x2 in x.split("-")
		print x2

Como vemos, la separación con split nos permite iterar sobre una cadena. Y esto es así porque realmente la función split devuelve una matriz (array) de cadenas.

Y a la inversa, teniendo otro array de cadenas, podemos unir sus elementos en un nuevo string con joinv.

init
	colores:array of string = {"rojo", "verde", "azul"}
	// Unimos los strings de este array
	// con distintos elementos (o con ninguno)
	m:string = string.joinv(":", colores)
	print m
	n:string = string.joinv(" ", colores)
	print n
	o:string = string.joinv("", colores)
	print o
rojo:verde:azul
rojo verde azul
rojoverdeazul

Otros métodos

init
	texto:string = "Bienvenido a Genie Doc."
 
	// texto a mayúsculas
	mayus:string = texto.up()
	print mayus
 
	// texto a minúsculas
	minus:string = texto.down()
	print minus
 
	// texto al revés
	reves:string = texto.reverse()
	print reves

El constructor de cadenas

Para finalizar por ahora con las cadenas, también comentar que existe un 'constructor de cadenas' (StringBuilder) que admite varios métodos para operar con ellas.

init
	paleta_colores:array of string = {"azul", "blanco", "rojo", "verde"}
 
	var paleta_nueva = new StringBuilder ()  // creamos el constructor
 
	for color in paleta_colores
		paleta_nueva.append (color + " ")  // agregamos nuevas cadenas al constructor
	stdout.printf ("La nueva paleta de colores tiene %s\n", paleta_nueva.str)
 
	paleta_nueva.prepend ("negro ")  // agrega al inicio del constructor
	stdout.printf ("La nueva paleta de colores tiene %s\n", paleta_nueva.str)
 
	paleta_nueva.insert (6, "morado ");//inserta cadena en una posición determinada
	stdout.printf ("La nueva paleta de colores tiene %s\n", paleta_nueva.str)
 
	print "%lu", paleta_nueva.len  // devuelve el número de caracteres
 
	// extrae uno o varios caracteres del constructor
	stdout.printf ("El primer caracter es %c\n", paleta_nueva.str[0])
	stdout.printf ("El primer color es %s\n", paleta_nueva.str[0:5])

Caracteres Unicode y ASCII

init
	a:string = "Geníe es mi lenguaje."
	print ("El cuarto caracter es %c", a[3])

En uno de los códigos anteriores hemos acentuado la letra i de Genie ¿qué pasará?

La compilación es correcta pero la ejecución nos devuelve:

[Invalid UTF-8] El cuarto caracter es \xc3

Esto pasa porque intenta devolvernos un carácter que está fuera de ASCII (como acentos, ñ, diéresis…).

Una manera que tiene Genie de manejar caracteres Unicode que están fuera del rango ASCII:

init
	Intl.setlocale( LocaleCategory.ALL, "" )
	a:string = "¿Sabías que mañana es mi cumpleaños?"
	print a

Anteriormente ya hemos visto otra manera de imprimir este tipo de caracteres:

init
	a:string = "¿Sabías que mañana es mi cumpleaños?"
	stdout.printf (a)

Pero esto no resuelve el problema de extraer e imprimir el carácter 'í' de Geníe. Vamos a probar una solución sencilla.

Sabiendo que con los corchetes no solo podemos conseguir un carácter, también una subcadena poniendo dentro el primer elemento a obtener, seguido de dos puntos y el primer elemento no extraído. Por ejemplo:

init
	a:string = "Genie es mi lenguaje."
	print ("%s me encanta.", a[0:5])
Genie me encanta.

Sabiendo eso, llegamos fácilmente a la solución:

init
	a:string = "Geníe es mi lenguaje."
	stdout.printf ("El cuarto caracter es %s", a[3:5])

Eso nos facilita mucho las cosas porque así extraemos un string (no un carácter), y eso sabemos imprirmirlo aunque no sea ASCII.

Aunque ahora nos ha resuelto el problema, esta solución no deja de ser simple. Con una función y algún bucle condicional podríamos llegar a otras soluciones más complejas.

Otra consecuencia de los dichosos caracteres ASCII:

init
	a:string = "Río"
	print ("La cadena tiene %d elementos", a.length)
	b:string = "Caña"
	print ("La cadena tiene %d elementos", b.length)
La cadena tiene 4 elementos
La cadena tiene 5 elementos

Esto es así porque tanto 'í' como 'ñ' ocupan 2 espacios cada una. Es importante tenerlo en cuenta. Una solución es contar caracteres y no espacios ocupados (en í, i ocupa un espacio y el acento ocupa otro):

init
	a:string = "Río"
	print ("La cadena tiene %d elementos", a.char_count())
La cadena tiene 3 elementos

Arrays

Acabamos de ver algunos ejemplos de arrays. Un array o matriz es una colección ordenada de elementos de un tipo de dato con un valor determinado.

Un array se crea mediante 'array of tipo_de_datos'. Por ejemplo, array of string o array of int seguido del tamaño fijo del array.

Podemos crear matrices de tamaño fijo indicando su tamaño entre corchetes. Así, para crear un array de 10 enteros:

var a = new array of int[10]

Se puede acceder a un elemento individual de un array a través de su índice de posición. Y podemos obtener la longitud de un array mediante nombre_array.length.

init
	var letras = new array of string[3]
	letras[0] = "abc"
	letras[1] = "def"
	letras[2] = "xyz"
 
	print(letras[0])
	print "%d", letras.length

Los arrays también pueden inicializarse directamente, y en ese caso caso no hay necesidad de especificar una longitud determinada (y en ese caso, se puede redefinir su tamaño y añadir nuevos elementos).

También se puede iterar sobre los elementos de una array con la instrucción for…in.

En el siguiente código se muestran ejemplos de las posibilidades de los arrays:

init
	paleta_colores:array of string = {"azul", "blanco", "rojo", "verde"}
	print "Una paleta con %d colores.", paleta_colores.length // 4
 
	print "Paleta de colores:"
	for color in paleta_colores // recorre los elementos del array
		print color
 
	print "Cambio color:"
	paleta_colores[1] = "negro" // cambia el valor de un elemento
	for color in paleta_colores
		print color
 
	var paleta_nueva = new array of string[3]  // array de 3 elementos
	var i = 0
	for color in paleta_colores
		paleta_nueva[i] = color // incorpora valores del otro array (solo 3)
		i++
	print "Nueva paleta:"
	for color in paleta_nueva
		print color
 
	lista_numeros:array of int = {1, 2, 3, 4, 5}
	stdout.printf ("Una lista de %d números: ", lista_numeros.length) // 5
	for n in lista_numeros
		stdout.printf ("%d ", n)

Usando los índices de posición de un array, podemos dividirlo con [inicio:fin] (el resultado de dividir una matriz no es una copia de los datos, es una referencia a éstos).

init
	a: array of int = { 2, 4, 6, 8 }
 
	b: array of int = a[1:3]
 
	print("%d", b.length) // 2
	print("%d", b[0]) // 4
	print("%d", b[1]) // 6

Se pueden añadir elementos a una matriz dinámicamente haciendo uso del operador += (esto sólo funciona para las matrices definidas localmente o privadas):

init
	e: array of int = {1, 2, 3, 4}
	e += 5
	e += 6
	e += 7
 
	print("%d", e.length) // 7
	print ("%d", e[4]) // 5

El tamaño de una matriz se puede variar llamando a resize():

init
	a: array of int = {1, 2, 3, 4}
 
	print("%d", a.length) // 4
 
	a.resize(6)
	print("%d", a.length) // 6
 
	print ("%d", a[4]) // 0

Los arrays pueden contener cualquier tipo de datos, incluso estructuras, y datos anidados o multidimensionales (array de arrays). Estas matrices apiladas, compuestas o matrices de matrices se definen con [,] o con [,,].

En estos casos, hay que tener en cuenta que cada elemento tiene que tener la misma longitud, y que length almacena la longitud de cada dimensión pero no de la matriz global. Además, a partir de una matriz multidimensional no se puede obtener una matriz de una dimensión, ni siquiera dividir una matriz multidimensional.

init
	var a = new array of int[3, 4]
	// a : array of int[,]  también válido
 
	a = {
		{2, 4, 6, 8},
		{3, 5, 7, 9},
		{1, 3, 5, 7}}
 
	print("%d", a.length[1]) // 4

Listas

Este tipo de datos avanzados (Gee collection datatypes), listas y diccionarios, no se admiten en las bibliotecas C estándar por lo que se requiere una biblioteca adicional denominada libgee.

Se trata de una pequeña biblioteca (libgee-0.8-2 y libgee-0.8-dev) que debe instalarse en el sistema y que también se lo tendrá que instalar cualquier persona que quiera ejecutar un programa así compilado.

Además, se debe indicar explícitamente al compilador Vala que lo vincule en la línea de comandos, por ejemplo:

$valac --pkg gee-0.8 test.gs

Cuando no se hace uso de listas o diccionarios, como hasta ahora en esta Wiki, entonces no necesita esta biblioteca.

Las listas son colecciones ordenadas de elementos, accesibles por índice numérico. Pueden crecer y reducirse automáticamente a medida que se añaden o eliminan los elementos. En esencia una lista es un array dinámico (y más potente).

Se crea una nueva lista con la misma sintaxis que otros elementos genéricos:

var mi_lista = new list of string

El código anterior crea una lista de cadenas pero también la podemos crear con otro tipo de datos.

Podemos acceder (por posición y por valor) y cambiar los elementos de una lista igual que hemos hecho con los arrays. E igualmente son iterables con for…in.

Pero además las listas disponen de varios métodos para operar con sus elementos:

  • add
  • clear
  • contains
  • get
  • index_of
  • insert
  • set
  • remove
  • remove_at
// compilar con valac --pkg gee-0.8 nombre_archivo.gs
init
	var invitados = new list of string
 
	// Añadimos elementos al final
	invitados.add("Pepe")
	invitados.add("Juan")
	invitados.add("Alicia")
 
	// Confirmamos elementos de la lista
	if invitados.contains("Antonio") == false
		print "Antonio no esta invitado."
	if invitados.contains("Juan") == true
		print "Adelante, Juan."
 
	// Rescatamos el valor de la posición 1 y luego le cambiamos el valor
	print "Invitado numero 1: %s", invitados.get(1)
	invitados.set(1,"Pablo")
	print "Invitado numero 1: %s", invitados[1]
 
	// Añadimos elementos en posiciones
	invitados.insert(1,"Antonio")
	invitados.insert(1,"Manuel")
 
	// Bucle para recorrer la lista
	for persona in invitados
		print persona
 
	// Borramos elementos por posición y por valor
	invitados.remove_at(4)
	invitados.remove("Antonio")
 
	// Obtenemos la posición de un elemento por su valor
	print "Posicion de Pablo en la lista: %d", invitados.index_of("Pablo")
 
	// asignamos a la posición 2 el valor Enrique
	invitados.set(2,"Enrique")
 
	for persona in invitados
		print persona

Para crear listas más complejas que contengan datos de distinto tipo podemos crear un objeto y después crear una lista de ese objeto. Aunque en esta Wiki todavía no hemos visto algunos de esos recursos, dejo un ejemplo que puede ayudar a hacernos una idea:

// compilar con valac --pkg gee-0.8 nombre_archivo.gs
// creamos la clase Agenda (un objeto)
class Agenda: Object
	nombre:string
	apellido:string
	fijo:int
	movil:int
 
contacto:Agenda
 
init
	// creamos una lista de la clase Agenda
	var lista_contactos = new list of Agenda
 
	// Instanciamos la clase Agenda
	contacto = new Agenda()
 
	// Introducimos datos
	contacto.nombre = "Pepe"
	contacto.apellido = "Viyuela"
	contacto.fijo = 913234234
	contacto.movil = 654123876
 
	// Añadimos los datos a la lista
	lista_contactos.add(contacto)
 
	// Repetimos con otro contacto:
	// primero debemos instanciar nuevamente Agenda
	// lo podemos hacer con el mismo nombre
	// después introducimos más datos
	// y añadimos a la lista de contactos
	contacto = new Agenda()
	contacto.nombre = "Ana"
	contacto.apellido = "Villanueva"
	contacto.fijo = 965765123
	contacto.movil = 654876876
	lista_contactos.add(contacto)
 
	// Recorremos la lista de contactos (lista del objeto Agenda)   
	for var x in lista_contactos
		print x.nombre + " " + x.apellido + " - " + x.fijo.to_string() + " - " + x.movil.to_string()

Este ejemplo se puede hacer más complejo anidando objetos en objetos y todos ellos añadiéndolos en una lista de objetos. Por ejemplo, se puede crear el objeto Telefono compuesto por fijo:int y movil:int y anidarlo en el objeto Agenda. Incluso podemos incluir listas dentro de esos objetos (por ejemplo, en Agenda podemos crear Grupo:new list of string que contenga “amigos, familia, trabajo”. Las posibilidades son muchas.

Diccionarios

Antes de usar los diccionarios recuerda lo que se comentó para las listas en cuanto a dependencias y compilación.

Los diccionarios (dict) son una colección desordenada de elementos accesibles por índices de tipo arbitrario. Pueden utilizarse como tablas de consulta y para asignar rápidamente valores.

Formalmente consisten en pares de una clave (key) con un valor. Los podemos imaginar como una tabla de dos columnas, siendo la primera columna las claves y la segunda los valores. Se accede a sus valores a través de sus claves

Al crear uno nuevo es necesario especificar los tipos de clave y valor:

var contacto = new dict of string,string
var agenda = new dict of string,int

Como en las listas, sus claves y valores también pueden iterarse con la instrucción for..in.

Se pueden añadir elementos usando el método add y también con agenda[clave]=valor.

// compilar con valac --pkg gee-0.8 nombre_archivo.gs
init
	// creamos el diccionario
	var capitales = new dict of string,string
 
	// incorporamos elementos
	capitales["Italia"] = "Roma"
	capitales["Rusia"] = "Moscu"
	capitales["Argentina"] = "Buenos Aires"
	capitales["Francia"] = "Tokio"
 
	// Rescatamos un valor por su clave
	print "La capital de Rusia es %s",capitales["Rusia"]
 
	// otra forma de añadir elementos
	capitales.set("Japon","Tokio")
 
	// cambiamos el valor de una clave
	capitales.set("Francia","Paris")
	print "La capital de Francia es %s", capitales.get("Francia")
 
	// comprobamos que existe una clave  *** OBSOLETO ***
	if capitales.contains("Argentina") == true
		print "Argentina esta en el diccionario."
 
	// comprobar que existe una clave
	if capitales.has_key("Argentina") == true
		print "Argentina esta en el diccionario."
 
	// recorremos las claves
	for pais in capitales.keys
		print pais   
 
	// recorremos los valores
	for ciudad in capitales.values
		print ciudad   
 
	// recorremos las claves y extraemos ambos
	for capi in capitales.keys
		print("Pais: %s -- Capital: %s", capi, capitales[capi])
 
	// eliminamos elemento
	capitales.remove("Rusia")
 
	// la clave puede ser una variable
	var k = "Italia"
	print "%s", capitales[k]
🔝