Mostrando entradas con la etiqueta Lista. Mostrar todas las entradas
Mostrando entradas con la etiqueta Lista. Mostrar todas las entradas

domingo, 5 de octubre de 2025

Lenguajes. Python.


Listas y tuplas



Lista como iterable

Ya hemos tenido ocasión de comprobar la utilidad que tiene el uso de un array como iterable en la automatización de procedimientos cuando trabajamos con OOo Basic. Uno de esos iterables en Python son las listas, de ahí el interés por saber iterar por ellas.

De hecho la iteración se presenta en Python más sencilla y a la vez más variada en cuanto procedimiento; y también más potente. Trataremos en esta entrada sobre las diferentes formar de iterar por una lista... y algo más.

Partiendo de la estructura for como instrucción básica para la iteración, la forma más sencilla de iterar es tomar como referente el elemento y como iterable la lista. Así, dada una lista (vg. los curso de E. Primaria)...

cursos = ['P1','P2','P3','P4','P5','P6']

... podemos recorrer la lista del siguiente modo:

for curso in cursos:

    print(curso)

Otra forma de iterar es  obtener el número de elementos de la lista mediante la función len() y usar la variable asociada (o la misma expresión) como límite superior del recorrido a realizar (1):

n = len(cursos)

for i in range (n):

    print(cursos[i])

En ocasiones nos puede venir bien recuperar a la vez el dato y el índice que, como elemento, ocupa en la lista. Para estos casos disponemos de un método específico:

for index, curso in enumerate (cursos):

    print(index, curso)

Otra situación especial, que no podemos contemplar como específica en OOo Basic, aunque podemos simularla de otro modo, es cuando disponemos de dos  o más listas y deseamos iterar por ambas, simultaneándolas:

cursos = ['P1','P2','P3','P4','P5','P6']
intervalos = ['2000/2001','2001/2002','2002/2003','2003/2004','2005/2006','2006/2007']

for curso, intervalo in zip(cursos, intervalos):

    print(intervalo, curso)

Finalmente también podemos iterar secuencialmente (2) por dos o más listas. Para ello primero importamos el módulo itertools y después usamos el método itertools.chain(), como vemos en este ejemplo:

import itertools
for item in itertools.chain(cursos, intervalos):
    print(item)

A partir de aquí ya podemos utilizar las listas para automatizar procedimiento al mismo nivel que hacemos en OOo Basic, sólo de con mayor facilidad y potencia. 


NOTAS 

(1) Obsérvese el modo diferente en que construimos la expresión de escritura en los dos métodos anteriores: en el primero es el referente (contador, si se prefiere, aunque en este caso no es esa su función principal) el que usamos como contenedor del dato (print(curso)), pero en el segundo identificamos directamente el elemento de la lista mediante su índice, siendo éste el valor actual del contador (print(cursos[i])). Como recordarás, este segundo método se asemeja más al modo en que trabajamos en OOo Basic.
(2) Secuencialmente se opone aquí a simultáneamente, lo que implica que zip() se opone a itertools.chain(). zip() es una de las funciones built-in que puedes consultar en [esta entrada]

sábado, 4 de octubre de 2025

Lenguajes. Python.


Listas y tuplas




Modificar el contenido de una lista

Trataremos en esta entrada cómo modificar los elementos que componen una lista mediante diferentes funciones y procedimientos completando lo visto en entradas anteriores, especialmente [en esta] cerrando una temática que lleva abierta desde hace tiempo.

Una vez definida una lista vacía (lista_1 = []) (1) tenemos dos funciones para añadir elementos:

Añadir elementos  al final (append())

lista_1.append('Rosa')

Añadir elementos en una posición intermedia (insert(indice,elemento))

lista_1.insert(1,'Cecilia')

Observa que para añadir elementos en una posición determinada debemos indicar como primer parámetro el índice (o posición) que deseemos que ocupe el elemento.

Si lo que deseamos es añadir un conjunto de elementos a la lista, todos a la vez, usaremos la función (extend([]))

lista_1.extend(['Maria','Celia'])

En realidad lo que añadimos es una lista a la lista anterior, motivo por el cual situamos los elementos entre corchete. De no hacerlo obtendremos error o un resultado inesperado (2)

También contamos con funciones para borrar o eliminar elementos de la lista:

Eliminamos el elemento identificado de la lista mediante (remove())

lista_1.remove('Rosa') 

El método (pop()) elimina por defecto el último elemento de la lista...

lista_1-pop() 

... pero si le pasamos como parámetro un índice válido de la lista elimina el elemento que ocupe esa posición

lista_1.pop(2) 

Pero el método más simple para borrar un elemento es usar la instrucción del

 del lista_1[0]

 

NOTAS

(1) También podría tratarse de una lista con un cierto número de elementos
(2) Lo segundo si añadimos una palabra ('Maria'). En este caso nos devolverá cada letra como elemento de la lista.

viernes, 16 de mayo de 2025

Python. Lenguaje.

Listas y tuplas.
Operadores


Los operadores son, [según sabemos], componentes de todo lenguaje de programación que permiten manipular los datos referenciados en las variables de diferentes maneras, según el tipo de operador y de datos de que se trate. También las colecciones de datos admiten la acción de operadores y listas y tuplas comparten los mismos.



Los operadores disponibles en listas y tuplas son los siguientes:
  • :, que permite obtener segmentos del contenido de ambas colecciones.
  • + y *, que permiten realizar las operaciones sobre listas y tuplas. Veremos cuáles.
  • in / not in, que permiten verificar la presencia / no presencia de determinado dato dentro del conjunto.
  • Y los operadores relacionales <, <=, >, >=, == y !=, cuyas funciones describiremos en su momento.
Aunque figure en primer lugar, no trataré el operador : en esta entrada, dada la relación que mantiene con el acceso a los datos. Por este motivo desplazo su análisis a la [entrada correspondiente]

Inicio este análisis con los operadores + y , los cuales cumplen funciones específicas con listas y tuplas, diferentes de las que permiten hacer con variables. 
  • El operador + permite la unión o concatenación de los elementos de varias listas , como cabe suponer por lo que sabemos de los operaciones aritméticos, nos permiten realizar operaciones de suma y multiplicación con listas y tuplas.
lista1 = list([1,2,3,4])
lista2 = list([5,6,7,8])
print(lista1 + lista2) -> devuelve [1,2,3,4,5,6,7,8]

  • Este mismo operador, aplicado a tuplas funciona exactamente igual, aunque lógicamente devuelve una tupla ((1,2,3,4,5,6,7,8))
  • La operación * tiene como efecto la repetición de la lista o la tupla sujeta a tal operación, siendo indiferente que el operador se sitúa antes o después de la lista o tupla. Así que print(3 * lista1) devuelve [1,2,3,4,1,2,3,4,1,2,3,4] y print(tupla1 * 2) devuelve (1,2,3,4,1,2,3,4). En ambos casos el número debe ser entero (ni 3.1 ni 2.4 se admiten como "multiplicadores")
  • Ambos operadores tienen el mismo efecto con independencia de la naturaleza de los elementos de la lista o de la tupla.
  • En ambos casos no está permitido el trabajo con listas y tuplas simultáneamente. No es posible ni lista + tupla, ni tupla * lista, ni ninguna otra combinación o permutación de elementos.
Los operadores in / not in permiten identificar si un datos forma parte o no de una lista o de una tupla. in y not in devuelven el booleano True o False según el resultado de la operación. Ejemplos para lista1 y tupla1 anteriores:

print(5 in lista1) devuelve False
print(1 in lista1) devuelve False

print(5 not in lista1) devuelve True
print(1 not in lista1) devuelve True

print(5 in tupla1) devuelve False
print(1 in tupla1) devuelve False

print(5 not in tupla1) devuelve True
print(1 not in tupla1) devuelve True

Los operadores relacionales permiten comparar dos listas entre sí, elemento a elemento y según criterios específicos en función del tipo de elemento. También devuelven True o False, según el resultado de la comparación. Veamos algunas comparaciones, tomando como referencia las listas y las tuplas creadas antes. Dada la multiplicidad de opciones me limitaré a unos pocos ejemplos, aunque los resultados se pueden hacer extensibles al resto de los operadores:

#Operadores relacionales con listas

print(lista1 == lista1) devuelve True

print(lista1 != lista2) Devuelve True

#Operadores relacionales con tuplas

print(tupla1 == tupla1) Devuelve True

print(tupla1 != tupla2) Devuelve True

#Operadores relacionales con listas y tuplas

print('Operadores relacionales con listas y tuplas')

print(lista1 == tupla1) Devuelve False por ser una lista y la otra tupla, aunque su contenido es el mismo (valores enteros 1,2,3,4 en ambos casos)

jueves, 15 de mayo de 2025

Python. Lenguaje.

Listas y tuplas
Creación automática de contenido


Aunque la forma más frecuente de crear colecciones de datos es introducirlos directamente mediante teclado o acceder a documentos en los que estos se encuentren disponibles, por diferentes motivos, en ocasiones es necesario crear colecciones de datos numéricos más o menos amplias cuyos elementos mantienen entre sí una relación, lo que hace que sea posible automatizar ese proceso. Para estos casos es muy útil conocer procedimientos de creación automática de colecciones de datos. Y existen... tanto para listas y como para tuplas.


El primer procedimiento para crear listas (y tuplas) automáticamente es el que utiliza la función range(). Esta función precisa tres parámetros: el valor de inicio, el valor final y el incremento. Un ejemplo: range(2,12,2) nos permite crear una colección de datos que inicia en el número 2 y finaliza en el 10 (el 12 no está incluido), incrementándose el valor inicial de 2 en 2 (2 - 4 - 6...).

Utilizamos el constructor de listas y creamos automáticamente una que cumple esos criterios. Si la lista es pequeña podemos hacerlo manualmente, pero si contiene muchos elementos es mucho más funcional usar la función range() que acabo de explicar: 
  • lista_1 = list(range(2,100,2)) crea una lista con todos los números pares comprendidos entre 2 y 100 (100 no incluido).
  • tupla_1 = tuple(range(2,100,2)) tiene el mismo efecto práctico, aunque en este caso se trata de una tupla, con todo lo que esto implica respecto a inmutabilidad.
Existen otros procedimientos que además de permitirnos crear colecciones de datos numéricos más complejas, también es necesario conocer para entender de qué se trata si nos encontramos con script que utilicen estas expresiones. Me estoy refiriendo a la creación de listas o tuplas que utilizan operadores, bucles y condicionales como parte de las expresiones que utilizan para crear la lista o tupla. Veamos algunos ejemplos: 
  • La lista_3 = [x * 2 for x in lista_1] es una lista de valores numéricos que utiliza la lista_1 (recuerda: números pares comprendidos entre 2 y 98) multiplicando por 2 cada uno de ellos. Mediante for x in lista_1 recorre esa lista.
  • La tupla_2 =tuple([x * 2 for x in tupla_1]) hace lo mismos pero creando una tupla y usando una tupla como base. Observa que ahora necesitamos usar el constructor de tuplas (antes no el listas) y usamos una combinación de ([]).
  • La lista_4 = [x**2 for x in lista_1 if x > 8] hace los mismo que lista_2, pero ahora añadimos como condición para la creación de la colección de datos que el valor original de lista_1 sea superior a 8 (if x > 8)
  • Finalmente, en lista_6 = [x * 3 for x in range(2,32,2) if x >10] utilizamos directamente la función range() para crear automáticamente la colección de datos de base (for x in range(2,32,2)) y mantenemos una condición: que los valores resultantes sean superiores a 10 (if x >10)
Y digo finalmente para no prolongar esta casuística más de lo necesario, porque aun caben otras combinaciones, incluyendo la de listas y tuplas y el uso de doble bucle en función de una doble variable (lista_5 = [x + y for x in lista_1 for y in lista_2]

Documento. En [este enlace] te dejo acceso a un script con ejemplos, incluidos los explicados en esta entrada.


martes, 13 de mayo de 2025

Python. Lenguaje.


Listas y tuplas
.

Acceso al contenido



Aunque listas y tuplas son colecciones de datos diferentes, ya dijimos que comparten muchas características y comportamientos; entre estos últimos están su contenido y el modo en que podemos acceder a él. 

El acceso a los elementos de las listas y las tuplas es un procedimiento de uso muy frecuente y de mucha utilidad práctica, por lo que interesa conocer cómo realizarlo. 

Ya sabemos cómo es el procedimiento básico por haberlo visto al hablar de ambas colecciones de datos (v.g. print(mi_tupla_a[0] nos devuelve en pantalla el primer elemento de la tupla), pero esto nos sirve cuando la tupla o la lista contienen elementos simples (vg mi_lista_a = [12,23,43,56]), pero, ¿ cómo accedemos a los elementos complejos, a las listas o tuplas anidados dentro de la lista o tupla principal?.

Un ejemplo de ello podría ser el siguiente: una lista que contiene "registros" que se comportan, a su vez como listas (1):

mi_lista_b = [['Lucía','Martínez Alonso','P4'],['Mario','Carcedo Suárez','P3'],['Carlota','Álvarez López','P2']]

Veamos mediante código distintas posibilidades de acceso al contenido de esta lista. Denominaré registro a cada una de las tres sublistas y dato a cada uno de los elementos de cada uno de los registros (dato -> nombre, dato -> apellidos, dato -> curso) 

#Acceso a la lista completa

print(mi_lista_b)

#Captura del primer registro

reg1 = mi_lista_b[0]

#Acceso al primer registro de la lista

print(mi_lista_b[0])

#Acceso al primer registro que pasamos previamente a reg1

print(reg1)

#Acceso al primer dato (dato -> nombre) de cada uno de los registros de la lista

reg_nombres = [[mi_lista_b[0][0]], [mi_lista_b[1][0]],[mi_lista_b[2][0]]]

print(reg_nombres)

 #Acceso al primer dato del nueva lista reg_nombres

print(reg_nombres[0])

#Otro modo de acceso (ahora reg_nombres_b es una lista de elementos imples

reg_nombres_b = [mi_lista_b[0][0], mi_lista_b[1][0],mi_lista_b[2][0]]

print(reg_nombres_b)

En este script estamos jugando con diferentes formas de acceder al contenidos de una lista compleja (lista de listas). Podemos diferenciar el modo de acceder al conjunto de la lista (print(mi_lista_b)) y a su primer elemento (print(mi_lista_b[0])). En el primer caso nos devuelve todos los registros de la lista (la lista con sus sublistas...

[['Lucía', 'Martínez Alonso', 'P4'], ['Mario', 'Carcedo Suárez', 'P3'], ['Carlota', 'Álvarez López', 'P2']]

... y en el segundo sólo el primer registro (la primera sublista)

['Lucía', 'Martínez Alonso', 'P4']

Si lo que queremos es acceder al primer datos de la primera sublista, deberemos expresarlo como sigue: print([mi_lista_b[0][0]) (nos devuelve -> Lucia). Observa que empleamos una pareja de corchetes para identificar el registro (en este caso el primero [0]) y a continuación una segunda pareja para identificar el datos que deseamos (en este caso también el primero [0])

Haciendo extensible este procedimiento, podemos crear una lista de listas que contienen como único registro el nombre de cada alumno...

reg_nombres = [[mi_lista_b[0][0]], [mi_lista_b[1][0]],[mi_lista_b[2][0]]]

... o, como alternativa, una lista de elementos simples con el mismo contenido

reg_nombres_b = [mi_lista_b[0][0], mi_lista_b[1][0],mi_lista_b[2][0]]

La diferencia es sutil pero fundamental: en el primer caso delimitamos doblemente con corchetes cada elemento, en el segundo no. Con el primer procedimiento obtenemos una lista de listas simples (de un sólo elemento o dato cada una)...

[['Lucía'], ['Mario'], ['Carlota']]

... y con el segundo una lista simple de tres elementos (2)

['Lucía', 'Mario', 'Carlota']

Además de acceder a elementos concretos de la lista o de la tupla, también podemos hacerlo a un subconjunto de datos mediante el operador :. De este operador ya hablamos en [esta entrada], pero dejamos apartado su tratamiento para relacionarlo con los procedimientos de acceso al contenido, motivo por el que corresponde ahora tratarlo.

Este operador presenta una formulación básica lista/tupla[inicio:fin] en la que se indica la posibilidad de delimitar el inicio del subconjunto y su final (mejor dicho, el elemento previo a su final). Por ejemplo, para lista0 = [1,2,3,4,5,6,7,8,9], print(lista0[0:3]) nos devuelve [1,2,3], dado que se cuentan los elementos empezando por 0 y el elemento posición 3 (->4) es 4, por lo que el anterior (elemento 2 -> 3). 

Ahora bien podemos omitir tanto el elemento inicio como el elemento fin. En el primer caso (vg. lista0[:3]) el resultado será el mismo que antes ([1,2,3]) ya que la ausencia del delimitador inicio implica partir del primer elemento de la lista (o de la tupla). Si omitimos el elemento final (vg lista0[2:]) implícitamente suponemos que deseamos ir desde el elemento inicio (posición 2 -> 3) hasta el final de la lista. En consecuencia, para lista0 -> [3,4,5,6,7,8,9].

Otra forma de acceder a segmentos del contenido de listas o tuplas es empleando índices negativos...

print(lista0[-5:-3]) -> devuelve [5,6]

print(tupla0[-2:]) -> Para tupla0 da igual contenido que lista0, esto es, devuelve (8,9)

... debido a que los valores negativos remiten al orden inverso en el acceso al contenido de listas y tuplas.


NOTAS

(1) El mismo ejemplo podríamos plantear mediante una tupla de tuplas, una lista de tuplas o una tupla de listas.
(2) Podríamos hacer exactamente lo mismo con una tupla y obtendríamos los mismos resultados. 

jueves, 13 de junio de 2024

Lenguajes. Python.

Listas.

Manejo básico

Dado que las listas son las colecciones de datos más simples con que cuenta Python, es necesario dedicar una entrada a aprender a trabajar con ellas.


Como dije [en su presentación], las colecciones de datos nos permiten almacenar un conjunto de datos asociados a una única referencia, mientras que con variable podemos acceder a un dato concreto. Python cuenta con cuatro tipos de colecciones de datos, siendo la lista la más simple y posiblemente también la más flexible de todas ellas.

Una lista es un conjunto ordenado de datos de diferente tipo (caracteres, cadenas, enteros, reales, booleanos) que pueden estar repetidos. Estas características, junto con las operaciones permitidas sobre ella, la convierten en un recurso muy flexible para el almacenamiento de datos.

La forma de declarar una lista es tan simple como se muestra en el ejemplo siguiente (1):

list_DiasSem = ["lunes","martes","miércoles","jueves","viernes"]

  • Se declara y asigna contenido de la misma forma y con el mismo operador que una variable, unificando ambos procesos en una única sentencia.
  • El identificador de la lista ([]) es su delimitador.
  • Los elementos de la lista se separan mediante comas.
  • Aunque en ésta todos los elementos son del mismo tipo, pueden ser de diferentes tipo.
  • Aunque podemos escribir la lista sin más, para poder acceder a ella o a alguno de sus elementos, es necesario asignarla a una variable (list_DiasSem)
  • Las listas pueden contener 0 elementos o todos los que deseemos.
Es importante entender que cada elemento de la lista ocupa una posición dentro de ella y que esta posición o índice es el que nos permite acceder a su contenido; de forma similar a cómo accedemos al contenido de una variable, pero respetando su sintaxis. Si para acceder al contenido de una variable únicamente tenemos que referenciarla, para acceder a un elemento determinado de una lista, debemos hacerlo especificando su índice. Si no indicamos ese índice se supone que estamos accediendo a toda la lista:
 
vPalabra = "casa"
lstPalabras = ["casa","mesa","armario"]
print(vPalabra) -> devuelve casa
print(lstPalabras[0]) -> devuelve casa
print(lstPalabras) -> devuelve ['casa','mesa','armario']

  • Si la lista lstPalabras estuviera vacía, print(lstPalabras[0]) provocaría un error (no hay elemento lstPalabras[0]), no así print(lstPalabras) -> devuelve [].
Podemos conocer el número de elementos de una lista mediante la función len(), cuyo resultado (return) podemos informar directamente mediante print(), o asignarla a una variable para su posterior uso. Por ejemplo, en list_DiasSem:

print(len(lis_DiasSem))                    -> Devuelve 5
vNumDiasSem=len(lis_DiasSem)
print(vNumDiasSemana)                  -> Devuelve 5

Una lista es modificable: podemos cambiar su contenido, añadir elementos o borrarlos, todo ello de forma muy simple:
  • Para cambiar un elemento, únicamente tenemos de reasignarle nuevo contenido haciendo uso de su índice; por ejemplo, siendo la lista...
vlstPalabras = ["casa","mesa","armario"]

 ... si queremos cambiar vlstPalabras[0] "casa" por "cama" diremos...

vlstPalabras[0] = "cama"

  • Para añadir un nuevo elemento a una lista podemos utilizar la siguiente expresión (el nuevo elemento se añade al final de la lista) (2):

 lstPalabras = lstPalabras + ["mesita"]

  • Para borrar un elemento de la lista usaremos la instrucción del de siguiente modo:

del lstPalabras[2] -> Borra el elemento "armario" que ocupa la tercera posición. 

Podemos trabajar con varias listas, por ejemplo dos, y unirlas en una tercera. Supongamos dos listas:

lstMuebles1=["mesa","silla","taburete"]
lstMuebles2=["sofá","armario","mesita"]

... la unión de ambas mediante la instrucción...

lstMuebles= lstMuebles1 + lstMuebles2

... nos da como resultado... 

print(lstMuebles) -> Devuelve ['mesa', 'silla', 'taburete', 'sofá', 'armario', 'mesita']

Además de unir dos listas en una tercera, también podemos considerar otras opciones, ya que una lista puede estar formada por una serie de elementos/datos (lista simple), pero también por una combinación de elementos y listas o por una colección de listas. La primera formulación ya la conocemos, tanto en su presentación más sencilla, como resultado de la unión o suma de dos listas; veremos a continuación las otras dos.

  • Lista resultante de la unión de una lista con otra mediante inclusión. Sea la lista inicial lstMuebles1=["mesa","armario"] y queremos modificarla para que esté conformada por la estructura de contenidos Muebles + Muebles de asiento; para ello creamos una segunda lista con muebles de asiento (lstAsientos=["silla","taburete","sillón","tresillo"]) y la incorporamos como elementos de la lista original:

lstMuebles=["mesa","armario",lstAsientos]

... obteniendo como resultado

print(lstMuebles) -> Devuelve ['mesa', 'armario', ['silla', 'taburete', 'sillón', 'tresillo']]

Obsérvese que lo que alteramos es la estructura de la primera lista, no unimos ambas, de ahí que los elementos de la segunda lista (que ocupa como elemento de la primera la posición 3 (lstMuebles[2]), aparecen delimitados mediante []. Esto es importante entenderlo, ya que el acceso a los contenidos de esta lista anidada se rigen por su doble posicionamiento: como elementos [2] de la lista principal y como elementos [x] de la lista secundaria. Así, si solicitamos el contenido de la posición [2], obtenemos...

 print(lstMuebles[2]) -> Devuelve ['silla', 'taburete', 'sillón', 'tresillo']

... por lo que si lo que deseamos es obtener el contenido de la posición 2 de la lista anidada (taburete), deberemos indicarlo con dos índices: el primero como elemento de la lista principal y el segundo de la lista anidada:

print(lstMuebles[2][1]) -> Ahora sí devuelve taburete

  • Un procedimiento similar se sigue para construir una lista formada por listas (lista de listas). Por ejemplo, supongamos que tenemos un conjunto de listas cuyos elementos representan subconjuntos de un conjunto principal y queremos representar esa pertenencia transformado el conjunto principal en una lista de listas, cuyos elementos son esos subconjuntos. 

lstFrutas=["pera","manzana","plátano"]
lstLegumbres=["garbanzo","lenteja","haba"]
lstHortalizas=["zanahoria","tomate","pimiento"]
lstVegetales=[lstFrutas,lstLegumbres,lstHortalizas]
print(lstVegetales) -> Devuelve [['pera', 'manzana', 'plátano'], ['garbanzo', 'lenteja', 'haba'], ['zanahoria', 'tomate', 'pimiento']]

El acceso a un elemento cualquiera de cualquiera de estas tres listas sigue el procedimiento explicado antes. Por ejemplo, para acceder al elemento 2  ([1]) de la lista 3 ([2]), se realiza con las siguiente instrucción:

 print(lstVegetales[2][1]) -> Devuelve tomate

El uso de bucles es una estrategia muy frecuente para recorrer los elementos de una lista y permite desarrollar procesos muy interesantes en la construcción de algoritmos. Esto es debido a la posibilidad de combinar el uso de las variables contador en combinación con los índices de las listas. Veamos un ejemplo sencillo: 

Supongamos una lista que contiene los nombres de los alumnos, una segunda que contiene sus apellidos y una tercera que contiene las calificaciones en una prueba.

lstNombre=["Julián","Pedro","Matilde", "Ana"]
lstApellidos=["Alonso Pérez","González López","Ramírez De Luis","Ordóñez Núñez"]
lstNotas=[8,9,7,4]

for i in range(4):
    print(lstNombre[i])
    print(lstApellidos[i])
    print(lstNotas[i])

Devuelve
Julián
Alonso Pérez
8
Pedro
González López
9
Matilde
Ramírez De Luis
7
Ana
Ordóñez Núñez
4

Pero en Python también podemos trabajar con el bucle for directamente sobre una lista, lo que simplifica enormemente la formulación de estas estructuras. Un ejemplo sobre una de las listas anteriores. Para ello es suficiente con emplear la expresión for item in NombreLista

lstNombre=["Julián","Pedro","Matilde", "Ana"]
for item in lstNombre:
    print(item,end=" ") -> Devuelve sucesivamente Julián  Pedro  Matilde  Ana

Una expresión similar a la anterior es la que usaremos para saber si un dato concreto forma parte de una lista. Sobre la lista anterior (lstNombre=["Julián","Pedro","Matilde", "Ana"]), para saber si el Carlos forma parte de la lista formularemos la siguiente instrucción: "Carlos " in lstNombre. Esta expresión devuelve un booleano (en este caso False, ya que Carlos no forma parte de lstNombre).

Podemos asociar esta búsqueda a un condicional y en función del resultado generar una bifurcación del algoritmo o, más sencillo, añadimos el elemento a la lista de nombres. Para ello primero deberemos asignar el resultado de "Carlos " in lstNombre a una variable:

vBoolA = "Carlos" in lstNombre

if vBoolA==False:
    lstNombre=lstNombre+["Carlos"]

print(lstNombre) -> Devuelve ['Julián', 'Pedro', 'Matilde', 'Ana', 'Carlos']

Mucho es aun lo que falta por aprender sobre las listas y su manejo, pero con lo visto ya hemos aprendido lo suficiente para incluirlas en nuestros ejercicios y proyectos (3).


NOTAS

(1) También podemos declarar una lista vacía (list_DiasSem = []) e ir llenándola de contenido según necesitemos. Esto puede ser de utilidad cuando desconozcamos de antemano el contenido de la lista. Para añadir elementos a una lista se usa la función append() (lista_DiasSem.append('Lunes'))
(2) Como sabes por 1, también puedes emplear la función append(). Más aun, es ésta la forma habitual de hacerlo.
(3) Entre otras fuentes de información, [esta referencia] es de mucho interés para saber más sobre las listas.

lunes, 25 de marzo de 2024

Textos. OOo Basic

Trabajar con listas.

Uno de los tipos específicos de texto que tenemos que crear con cierta frecuencia  son los listados, bien como documentos en si mismos, bien como elementos de un documento más amplio. Para hacerlo mediante teclado (que suele ser lo común) existen tres opciones básicas: crear la lista desde cero, copiar y pegar los ítem recurriendo a un documento externo que contiene la lista maestra o utilizar esa lista maestra como plantilla y, al contrario que en los casos anteriores, eliminar los ítem que no interesen. Aunque la tercera fórmula es el modo de trabajo que más se acerca al objetivo de la automatización, todas ellas, en realidad, están muy lejos que acercarse a este objetivo. Usar macros y script (OOo Basic) es la única forma en que realmente se facilitar la tarea.


Aunque no siempre es posible ni conveniente utilizar macros y/o script para automatizar la creación de un listado (1), hay razones para pensar que son muchas las circunstancias en las que sí merece la pena crear procedimientos informatizados para ayudarnos en la tarea. esto es especialmente cierto cuando contamos con un listado previo y relativamente estable y más aun si ese listado es de cierta extensión. Ejemplos hay muchos: listado del alumnado con NEAE, relato de los asistentes a una reunión de la UO (2), listado de indicadores de evaluación a incluir en una PTI...

En cualquier caso, y para lo que ahora nos importa, disponer de procedimientos de generación informatizada de listados nos va a ayudar a simplificar el trabajo, aunque requiera uno previo.

No me voy a detener demasiado en el aspecto formal de la creación de un listado, que podemos resolver mediante el uso de tablas o mediante la activación de las marcas del ítem de lista (vg. un número índice o una bolita) por medio de una macro. Pero tampoco quiere dejar de explicar cómo hacerlo, así que dedicaremos el inicio de esta entrada a explicar esta forma de trabajo mediante macros simples y script.

Como sabemos, las listas son de dos tipos genéricos: alfanuméricas y gráficas. Las dos macros simples que las generan son las siguientes:

 Alfanumérica:

 dim args1(0) as new com.sun.star.beans.PropertyValue
args1(0).Name = "On"
args1(0).Value = true
dispatcher.executeDispatch(document, ".uno:DefaultNumbering", "", 0, args1())

Gráfica (en este caso de bolita)

dim args1(0) as new com.sun.star.beans.PropertyValue
args1(0).Name = "On"
args1(0).Value = Valor
dispatcher.executeDispatch(document, ".uno:DefaultBullet", "", 0, args1())

Ambas trabajan con argumentos booleanos (arg().Value), así que la conversión de estas macros simples en subrutinas para ser empleadas desde un script, supone implementar un argumento (variable) de tipo Booleansub ListaSimple (Valor As Boolean) que deberemos incluir en la llamada a la subrutina como en este ejemplo: ListaSimple(True). Cuando queramos finalizar el listado, deberemos llamar de nuevo a la subrutina y cambiar el argumento a False.

Además de este código, también necesitamos el código (macro o script) de escritura y el salto de línea al finalizar el ítem... 

dispatcher.executeDispatch(document, ".uno:InsertPara", "", 0, Array())

Podemos anidar listas (e ítem) mediante el uso de tabulador, generando un incremento del sangrado que facilita la estructuración de la lista en ítem principal e ítem secundario. Para ellos haremos uso de la misma macro-subrutina de escritura, pasando como argumento el carácter especial Chr$(9). A continuación te muestro el script que nos permite generar una lista (los inicios de la misma) que contiene otra anidada. Hacerla más amplia y funcional es cosa de escribir más líneas de código:

Sub CrearLista

VarSis

ListaSimple(True)

Escribir("Listado de alumnos con NEE")

SaltarLinea

ListaSimple(False)

Escribir(Chr$(9))

ListaSimpleNum(True)

Escribir("Jaime Ortega López")

End Sub

... y este es el resultado. Simple por haberme ahorrado esfuerzos, pero suficiente para explicar lo que me interesa.

Con esto y con lo que sabemos sobre escritura y sobre interfaces podemos crear listas automatizando parte del proceso, aunque realmente lo más pesado del trabajo queda pendiente de automatizar. Un paso más hacia ese objetivo supone hacer uso de matrices, bucles e incluso condicionales. He aquí un ejemplo:

Sub Semana

VarSis
Dim DiasSemana(6) As String
Dim i As Integer


DiasSemana(0) = "Lunes"
DiasSemana(1) = "Martes"
DiasSemana(2) = "Miércoles"
DiasSemana(3) = "Jueves"
DiasSemana(4) = "Viernes"
DiasSemana(5) = "Sábado"
DiasSemana(6) = "Domingo"


ListaSimple(True)
Escribir("Días de la semana")
SaltarLinea
ListaSimple(False)
Escribir(Chr$(9))
ListaSimpleNum(True)


For i = LBound(DiasSemana()) To UBound(DiasSemana())
Escribir(DiasSemana(i))
If i < UBound(DiasSemana()) Then
SaltarLinea
End If
Next

End Sub


Como puedes observar, este script se divide en cuatro partes:

  • En la primera se llama a VarSis que nos permite trabajar con macros y se declara una matriz (DiasSemana(6)) y una variable contador (i)
  • En la segunda se asignan valores a cada elemento de la matriz
  • En la tercera se llama a un conjunto de macros para escribir la primera línea del código, saltar a la línea siguiente, generar un  espacio de tabulación e implementar el formato numérico de la lista (3)
  • La cuarta parte es la de mayor complejidad y novedad. En síntesis recorremos la matriz con un bucle For utilizando como referencias los valores inicial y final de la estructura de la matriz mediante las funciones LBound() y UBound() y llamamos a la macro de escritura asignando como parámetro cada un de los elementos de la matriz. El condicional If evita que se escriba un elemento de la lista innecesario, si bien su función inmediata es escribir los elementos numerales del listado mediante la macro SaltarLinea.

Muestro a continuación el resultado. No parece gran cosa, pero que supone la escritura automática de una lista:

 

Nos falta dar un paso más que básicamente consiste en dar la posibilidad de automatizar la escritura de una lista resultante de la selección de ítem de otra lista. Viene a ser el equivalente a trabajar sobre un documento-base que contiene a lista completa de la que vamos borrando los elementos que no nos interesan. De este modo conseguimos generar listas personalizadas, con todos los posibles usos prácticos que podemos intuir (4)

Este es el script. Por simplicidad sustituyo la escritura en el documento por el mero visionado de los elementos de la lista mediante MsgBox, pero el principio de funcionamiento del script no supone una variación sustancial. Esta simplificación permite que nos centremos en lo fundamental del funcionamiento del script.

Sub MatrizSemana

Dim DiasSemana(6) As String
Dim MisDias () As String 
Dim Dia As Integer
Dim i As Integer, b As Integer


DiasSemana(0) = "Lunes"
DiasSemana(1) = "Martes"
DiasSemana(2) = "Miércoles"
DiasSemana(3) = "Jueves"
DiasSemana(4) = "Viernes"
DiasSemana(5) = "Sabado"
DiasSemana(6) = "Domingo"


b = 0
For i = LBound(DiasSemana()) To UBound(DiasSemana())
Dia = MsgBox (DiasSemana(i),4,"MIS DIAS")
If Dia = 6 Then
ReDim Preserve MisDias(b)
MisDias(b) = DiasSemana(i)
b=b+1
End If
Next

MsgBox "Número de días seleccionados: " & CStr(UBound(MisDias()) + 1)

For i = LBound(MisDias()) To UBound(MisDias())
MsgBox MisDias(i)
Next

End Sub

Como puedes ver es similar al script anterior, aunque con una diferencia fundamental: ahora se trata de "pasar los datos de la matriz original a "nuestra" matriz (la que nos interesa) seleccionando de la primera los ítem que nos resultan de interés. Este proceso de traslado de datos tiene cierta complejidad, por lo que requiere una explicación. Pero vayamos por partes. 

Apreciamos cuatro fases en el desarrollo del script (5), la primera de declaración de variables y matrices, la segunda de asignación de contenido a la matriz-origen de datos, la tercera de selección de contenidos para generar la matriz de destino (la "personalizada") y la cuarta para la salida de datos de esa matriz-destino.

A diferencia del modelo anterior, en la primera parte del script declaramos dos matrices (DiasSemana(6) MisDias ()), cuyos nombres son claramente explicativos de su función. Contiene además una variable-puente para pasar los datos de una matriz a otra (Dia) y dos contadores (i y b): el primero es empleado en el funcionamiento de los bucles y el segundo será explicado en su momento (6).

La segunda parte es idéntica a la equivalente en el script anterior, pero la tercera (primer bucle For) supone un cambio fundamental respecto a ese script: 
  • El contenido de la variable de paso Dia se asocia a la función MsgBox() para que el usuario seleccione (comandos /No) los ítem que le interesen de la matriz principal, la cual se usa como información en el cuerpo del MsgBox: 
Dia = MsgBox (DiasSemana(i),4,"MIS DIAS")

  •  El condicional que sigue permite condicionar la incorporación del dato en función de la respuesta anterior (). De no cumplirse esa condición no se produce el paso del valor a la matriz-destino: MisDias(b) = DiasSemana(i)
  • Pero, además, en el cuerpo de ese condicional, y antes de la asignación antes señalada, redimensionamos la matriz-destino, manteniendo su contenido (ReDim Preserve MisDias(b)). Esto es fundamental para controlar que la matriz-destino no incluya elementos no deseados (posiciones vacías).
  • Nos debemos fijar ahora en el uso del contador b: hemos asignado un valor inicial antes del inicio del bucle (b = 0), lo utilizamos como índice de la matriz de destino en sustitución del contador i que utilizamos como índice en la matriz-origen, tanto para el redimensionado como en la asignación, y finalmente y también dentro del condicional, incrementamos su valor como contador (b=b+1).
  • El resultado de esta estructura es que el usuario selecciona/rechaza cada uno de los ítem de la matriz-origen (7), pero sólo se incorpora a la matriz-destino el ítem seleccionado. El MsgBox que sigue permite comprobar el número de elementos de la matriz-destino una vez finalizado el proceso de selección (MsgBox "Número de días seleccionados: " & CStr(UBound(MisDias()) + 1)) (8)
La cuarta parte (y final) del script es en realidad muy simple: un segundo bucle recorre ahora la matriz-final mostrando su contenido mediante un MsgBox. Si en el primer bucle el uso de las funciones LBound() y UBound() puede considerar opcional (ya que conocemos las dimensiones de la matriz-origen), en este segundo bucle es prescriptivo, ya que, por definición ignoramos las dimensiones de la matriz-destino.

Para finalizar, y por resumir, en esta entrada hemos aprendido a crear listados automatizando su escritura pero, lo que es más interesante, su contenido. Este uso del código nos puede ser de mucha utilidad, aunque requiere crear la o las matrices originales y recorrerlas en su integridad, si bien existe la posibilidad (aquí no contemplado) de salir del bucle de selección una vez satisfechos los criterios de selección que tengamos en mente.

NOTAS.

(1) No veo la posibilidad ni la conveniencia de hacerlo cuando la lista es sencilla y no partimos de una definición clara de los ítem. En esos casos el trabajo de crear el procedimiento informatizado puede ser mayor que el que haremos escribiéndola directamente.
(2) Este en realidad es un ejemplo dudoso, ya que se puede incluir el listado completo en la plantilla del acta de la RUO y proceder simplemente suprimiendo los no asistentes. No obstante sí puede ser de interés automatizar la elaboración del acta, con lo que la inclusión del listado de asistentes será parte del proceso, aunque no necesariamente si partimos de un documento-modelo que contenga la lista de componentes de la UO.
(3) Todo ello reproduce el modelo de uso explicado al inicio de esta entrada.
(4) La complejidad del contenido depende especialmente de los contenidos de la propia lista: desde una sencilla y de pocos elementos, hasta una de muchos componentes, como puede ser el listado de las pautas DUA o de los criterios de evaluación de un área curricular.
(5) Eliminar la escritura en el documento permite esta reducción del contenido del script. En otro caso sería necesario, cuanto menos, incluir una llamada a la subrutina de escritura y si este proceso lo realizáramos empleando macros y manteniendo la visualización que hemos generado en las formas anteriores, implicaría el añadido de los elementos que utilizamos en el script anterior.
(6) En este script no se llama a VarSis por no ser necesario para el funcionamiento esperado. De querer utilizar macros para escribir en el documento, deberíamos incluirlo.
(7) El procedimiento de ir seleccionado ítem por ítem hace que sea un tanto lento, y más aun si el número de ítem es elevado. Es posible salir de bucle mediante la instrucción Exit asociada a un condicional, y también es posible que existan formas de condicionar la presentación o no de determinados ítem de la matriz-origen. La primera fórmula no la uso en este caso y la segunda, en este momento desconozco cómo implementarla.
(8) Dado que es ésta su única función es perfectamente posible prescindir de él en la formulación final del script, pero es muy útil durante la fase de pruebas, motivo por el cual lo he mantenido. Obsérvese que hago uso de la función UBound(MisDias()) para conocer el valor de posición del último elemento de la matriz-destino, añadiendo +1 para que el resultado sea directamente comprensible (el número exacto de elementos de la matriz-destino). Esto es debido a que, como sabemos, el primer elemento de una matriz se identifica como 0.