1. Micromundos para desocupados
Enrique Araújoviedo, PFPD Ambientes Virtuales de Aprendizaje, Bogotá, D.C., 2009.
Parte II:
para ver
asigna [ variable menosúltimo menosúltimo [ LoBueno LoMalo LoFeo ] ]
muestra : variable
fin
Las cosas buenas no deberían ser tan complicadas como el anterior procedimiento. Y en eso
hay que reconocerle a Logo el valor añadido que le otorga a la herramienta “micromundos
pro”. Realmente no importa que esté mal traducido o que las palabras no sean las más
bonitas, lo que se puede hacer con Logo es sencillamente excepcional. Todo depende de uno,
del programador.
Una parte desconocida de Logo es precisamente el potencial que tienen las listas, no sólo
como herramientas de programación sino como objetos de aprendizaje y objetos de desarrollo
de pensamiento complejo.
En esta sección vamos a ocuparnos un poco de las listas y vamos a esforzarnos por mostrar la
belleza que estas le proveen a Logo y el potencial lógico de programación.
Puesto que esta introducción está dirigida para profesores no nos ocuparemos de los aspectos
de la enseñabilidad (el cómo enseñar a pensar con listas a los estudiantes); eso lo haremos
más adelante.
Una lista es un objeto que tiene cabeza y cola (algo así como un primero y un último
elemento); pero una lista puede contener un solo elemento y en ese caso el único elemento es
cabeza y cola a la vez (imagínese una fila en un supermercado; imagínese que sólo hay un
cliente. Ese cliente, el único en la fila, es el primero y el último a la vez). Una lista también
puede estar vacía, es decir que no tiene ningún elemento. Este por supuesto es el caso trivial,
pero necesario (imagínese otra vez la fila en el supermercado; suponga que hay un solo cajero
atendiendo, en algún momento el cajero se desocupará porque ha atendido a todos los de la
fila y se queda a la espera que aparezca un cliente; durante ese tiempo, la fila está vacía).
Repasemos: existe una lista vacía; existe una lista con un único miembro que es cabeza y cola y
de ahí en adelante es claro que una lista con más de dos miembros tiene un primer elemento
(la cabeza) y un último elemento (la cola). Ahora como de lo que se trata es de listas de datos
y sabemos que los tipos de datos son numéricos, alfabéticos y alfanuméricos parece obvio
pensar que los elementos de una lista son estos objetos; pero no, hay todavía más: los
miembros de una lista pueden ser también listas. Para concretar, diremos que los elementos
de una lista pueden ser “palabras” (objetos que no son listas) y “listas”. Así pues una lista es
una colección ordenada (y esto es muy importante ya que las diferencia de los conjuntos) de
palabras y/o listas. Las “palabras” pueden ser un dato numérico, un dato alfanumérico, un
2. dato alfabético e incluso una operación o un conector (lógico). Con las listas podemos
construir estructuras de datos que incluyen otras palabras y listas.
Las listas se representan mediante corchetes cuadrados y sus elementos están contenidos
dentro de éstos, así: [ 1 2 3 ] es una lista con tres miembros, a saber los datos numéricos
1, 2 y 3. En ésta lista la cabeza es el dato numérico 1 (uno) y la cola el dato numérico 3 (tres).
Ahora si consideramos la lista [ 1 [ 2 3] ], se observa que es una lista que tiene una
“palabra” (el dato numérico 1) y una “lista” (la lista que contiene los datos numéricos 2 y 3).
Se tiene pues que la cabeza de la lista es el dato 1 y que la cola es la lista [2 3]. En ésta lista la
cabeza es el dato 2 y la cola es el dato 3. Así que podemos decir que “la cabeza de la cola (la
lista [2 3]) es 2” y que “la cola de la cola es 3”. En éste simple ejemplo se puede observar
rápidamente la complejidad que entrañan las listas. Esto permite establecer que las listas [ 1
[ 2 3] ] y [ 1 [ 3 2] ] son distintas ya que “la cabeza de la cola” de la primera lista
es distinta de “la cabeza de la cola” de la segunda lista y, de modo semejante “la cola de la
cola” de la primera lista s distinta de la “cola de la cola” de la segunda lista.
Esto siempre es así, pues si tiene la lista de listas vacías [ [ ] [ [ ] [ ] ] ] y se
compara con la lista de listas vacías [ [ [ ] [ ] ] [ ] ] se observa que son distintas
tanto las “cabezas” como las “colas”.
Pero obviamente las listas son más claras si tienen objetos que nos sean familiares: [1 [1
2][7 [a b]]]
Una pregunta inmediata es ¿cuántos elementos posee una lista? Aunque la pregunta parece
sencilla de responder, entraña cierta dificultad. Veamos. La lista anterior contiene tres
elementos: “una palabra, el número 1”; la lista [1 2] y la lista [7 [a b] ]. El segundo y
tercer elemento son también listas.
Otra pregunta básica es la diferencia entre una palabra y una lista; parece obvia. Gracias
precisamente a la posibilidad de contar los elementos de una lista podemos establecer una
denominación para cada elemento: a cada elemento de una lista lo llamaremos un ítem y está
definido por su posición en la lista; así el ítem 4 corresponderá al elemento que se encuentra
en la cuarta posición de la lista que como se dijo puede ser una lista o un caracter. En una
palabra sus elementos son exclusivamente caracteres. Esta es la primera diferencia como
objetos generales. Para Logo las diferencias pueden ser no tan evidentes para el usuario, por
ejemplo Logo no reconoce de la misma manera una lista vacía y una palabra vacía, aunque
devuelva lo mismo (un espacio). Se observa mejor esta situación si consideramos una lista que
contenga una sola palabra y una sola palabra:
muestra "palabra muestra [palabra]
palabra palabra
Aunque el resultado sea el mismo, son procesados como dos diferentes tipos de datos.
Esto hace que las operaciones que actúen sobre una lista actúen sobre los ítems de modo
distinto a como lo harían operaciones similares con las palabras.
3. Es claro que las operaciones necesarias en una lista deben identificar los ítems, así que las
primeras operaciones deben identificar el primero y último elemento sin por ello dejar de
importar sus características. La operación en sí actúa sobre la posición y luego sobre el
contenido ubicado en dicha posición; pero cualquier operación de este tipo debe disponer de
una información previa: la cantidad de miembros de una lista; pues ¿cómo saber cuál es el
último si no se dispone de ese dato?
Una estructura operativa de posicionamiento sobre listas en general, responde a una regla de
datos como: (cantidad de miembros: n; identificar primer elemento: ubicar puntero n-1
posiciones anteriores), si se trata del primer elemento. Si se tratara de ubicar el ítem k-ésimo la
estructura sería: (cantidad de miembros: n; establecer si k<n; en caso afirmativo ubicar puntero
(n-k) posiciones anteriores contadas desde el último elemento, en caso negativo indicar
imposibilidad de actuar; si el caso es positivo, identificar k-ésimo miembro). Pareciera
engorrosa la estructura ya que los humanos no pensamos así, ya que podemos pensar con
datos incompletos (información borrosa) o asumir progresiones ascendentes sin importar la
existencia de un último elemento. En las estructuras de datos orientadas a máquinas (tipo Von
Newmann) ello podría implicar poner a la máquina en un loop infinito o en una situación en la
que no puede parar. Pero bueno, los compiladores nos resuelven estos problemas para no
tener que enfrentarnos directamente a ellos y Logo dispone de dos operaciones que nos
ayudan en esta actividad: la primera sirve para contar los elementos de una lista y la segunda
para ubicarse en la posición de un ítem en particular. Otra cosa es extraer el dato allí
contenido. Para ello se debe disponer de una (o unas operaciones de extracción) cuya
estructura de datos es semejante a como sigue: ((cantidad de miembros: n; establecer si k<n;
en caso afirmativo ubicar puntero (n-k) posiciones anteriores contadas desde el último
elemento, en caso negativo indicar imposibilidad de actuar); (si el caso es positivo, identificar k-
ésimo miembro: establecer si el elemento es nulo –vacío-, si es una palabra o una lista;
establecer la cantidad de memoria necesaria para el elemento y definir una variable temporal
que contendrá una copia del elemento; copiar el elemento en la memoria temporal); si existe la
petición mostrar contenido de la memoria temporal –o sea el elemento-).
Estudiar la estructura interna de las listas y cómo actúan las operaciones sobre ellas es
necesario si se quiere ir más allá de la simple manipulación de las listas; la programación
requiere en ocasiones pensar como razona el diseñador de los compiladores (en éste caso el
intérprete, ya que Logo es un lenguaje interpretado y no compilado –que es una verdadera
lástima no disponer de un compilador Logo-).
Si se disponen de las operaciones de extracción deben disponerse también de las operaciones
de inserción (operaciones inversas) [obsérvese la semejanza con el manejo de las bases de
datos; pero cuidado, las listas no son bases de datos aunque podemos usar listas para elaborar
bases de datos –esto lo trataremos en un aparte especial-].
Resumiendo el manejo de las listas implica entender que se tiene a) un apuntador hacia el
primer elemento de la lista y b) un apuntador hacia el resto de la lista. En particular Logo
cuenta con un apuntador hacia el primer elemento de la lista: FIRST (en micromundos pro:
primero), con un apuntador hacia el último elemento de la lista: LAST (en micromundos pro:
último). La lista [ A B C D ] se almacena como una sucesión de apuntadores. En el
4. primer nodo hay un apuntador hacia el elemento A y un apuntador hacia otro nodo, en éste
hay un apuntador hacia B y otro hacia el nodo siguiente; el último nodo apuntará hacia el
elemento D y hacia ninguna parte –un nodo nulo-. En el siguiente árbol se muestra el
almacenamiento de la lista:
Figura 1. Almacenamiento de la lista [ A B C D ]
Obsérvese ahora el manejo de los apuntadores en la lista: [[A B] [C D]]:
Figura 2. Almacenamiento de la lista [ [A B] [C D] ]
Pregúntese ahora cómo serán los árboles de las listas: [A [B C D]], [A [B] [C D]],
[[A B C] [D]], [[A] [B] [C D]].
Las primitivas Logo correspondientes a las operaciones mencionadas son:
Primitiva definición ejemplo devuelve
primero Devuelve el primer elemento de una lista. primero [A [B] [C D]] A
último Devuelve el último elemento de una lista. último [A [B] [C D]] [C D]
menosprimero Devuelve la lista sin el primer elemento. menosprimero [[A B C] [[D]]
[D]],
menosúltimo Devuelve la lista sin el último elemento. menosúltimo [[A B C] [[A B C]]
[D]]
ítem Devuelve el elemento especificado de una ítem 2 [A [B] [C D]] [B]
lista.