|
|
Con el fin de propiciar el nacimiento de la biblioteca parametrizada STL [STL-95], en su libro The Design and Evolution of C++ [Str-84], Bjarne Stroustrup, el máximo exponente de C++, hace la siguiente afirmación:
"Las plantillas fueron consideradas esenciales para el adecuado diseño de clases de contenedores... Para muchas personas, el mayor problema de C++ es la falta de una librería estándar extensa. Un problema mayor al producir esa librería es que C++ no provee una facilidad suficientemente general para definir "clases contenedoras" como listas, vectores y arreglos asociativos."
Esta afirmación de Stroustrup no es correcta, como se ha
demostrado en este trabajo al
implementar
contenedores parametrizables con la ayuda de la unidad Pascal
Binder.pas
, en
especial con la lista implementada en
List.pas
. En la
sección 7.1, precisamente
con la
Figura 7.4, se describe en
detalle la
técnica que hay que usar para
implementar contenedores parametrizables usando
Binder.pas
.
La hipótesis inicial de trabajo para esta investigación ha quedado demostrada, pues efectivamente no se necesita de avanzadas construcciones semánticas para lograr la parametrización de contenedores. Por eso se puede afirmar que es posible reutilizar contenedores con lenguajes de semántica limitada, como, por ejemplo, Pascal, pese a que no cuenten con avanzadas construcciones sintácticas como plantillas o paquetes genéricos. Por eso es válido hacer estas afirmaciones:
Es posible implementar contenedores parametrizables sin usar plantillas ni paquetes genéricos. |
Si es posible implementar contenedores polimórficos si el lenguaje no tiene apoyo directo para parametrización |
El lenguaje de programación más usado para
implementar la mayor parte de los sistemas de computación
avanzados es C++. Como C++ tiene soporte directo para todas las
construcciones que se usaron en la implementación Pascal
aquí descrita, se puede afirmar que los resultados
obtenidos se pueden obtener también para el lenguaje C++.
Lo interesante será obtener una biblioteca de contenedores
reutilizables, similar a
STL, pero con la particularidad de
que la reutilización se haga a nivel de lenguaje de
máquina, y no a nivel del código fuente de los
algoritmos. En C++ se usarían las
plantillas para obtener
resultados similares al uso de la construcción
TYPECAST
discutida en la
sección 7.9. Algunas de las
ventajas de implementar STL con el apoyo de la tecnología
que Binder.pas
representa son las siguientes:
La principal deficiencia de la biblioteca STL, y en general de todas las bibliotecas tradicionales de contenedores, es que la reutilización se logra duplicando los algoritmos al instanciarlos por medio del polimorfismo textual que comporta el uso de plantillas o paquetes genéricos. Esta deficiencia se corrige si se usan las ideas desarrolladas en este trabajo.
9.1 Contribuciones de esta disertación
La contribución más importante de este trabajo
está plasmada en la
implementación
del contenedor
List.pas
. Al implementar
los contenedores siguiendo las
ideas desarrolladas en esta
investigación, se alcanzan dos objetivos muy importantes.
Primero, la
reutilización se da
a nivel del código compilado, y segundo, se evita
distribuir los códigos fuente de los algoritmos. Al
alcanzar estos dos objetivos, se allana el camino para que luego
sea posible incluir dentro del sistema operativo, como un servicio
adicional, una biblioteca de contenedores que pueda ser compartida
por todos los programas, lo que ayudará a mejorar la
eficiencia de los sistemas de cómputo. Esto no es posible
si la reutilización de contenedores se continúa
haciendo en la forma tradicional, usando
parametrización textual
mediante el uso de plantillas. Los resultados obtenidos descansan
en un descubrimiento muy importante:
| |
El campo de enlace que aparece en cada uno de los valores almacenados en un contenedor no le pertenece al elemento almacenado, sino que más bien le pertenece al contenedor, que es el encargado de manipularlo. |
Desde esta óptica, cada elemento contiene un campo que le
permite enlazarse en el contenedor: por eso, al implementar las
operaciones del elemento, no es necesario saber cómo
está hecho el campo de enlace. En otras bibliotecas nunca
se ha hecho esta diferenciación, lo que ha impedido
compartir el código compilado de los algoritmos y, por
ende, ha resultado en una reutilización menos efectiva que
la que se ha logrado aquí. Los contenedores parametrizados
con Binder.pas
se distinguen de los de cualquier otra
biblioteca de programación porque siempre comparten la
implementación de los algoritmos, pero sin usar
semántica de referencia
para lograrlo.
El análisis de rendimiento realizado en la
sección 8.4 muestra que el
impacto de
Binder.pas
en el
rendimiento de los programas es prácticamente
insignificante, equivalente a agregar en algunas ocasiones un
llamado indirecto a una subrutina. Aunque pueden existir programas
en que ese precio sea inaceptable, en general los beneficios de
Binder.pas
justifican con creces el costo.
En la implementación de
Tree.pas
se ha
reutilizado la lista, sin necesidad de hacerle cambios al
código fuente o al código compilado del
módulo
List.pas
, lo que es
prueba fehaciente de que se pueden construir contenedores
reutilizables usando como herramienta a
Binder.pas
. Otras
contribuciones importantes que se derivan del trabajo realizado
son las siguientes:
friend
), como se muestra en la
Figura 3.16.Binder.pas
.Binder.pas
, y de otros
módulos Pascal de apoyo, para parametrizar
contenedores.CTPinst
, que puede usarse junto a los
contenedores parametrizados a través de
Binder.pas
.TYPECAST
para facilitar
la implementación de contenedores a través de
módulos similares a Binder.pas
.Binder.pas
son totalmente
independientes de los elementos almacenados en el contenedor, lo
que facilita mucho reutilizarlos en cualquier contexto. Gracias
al uso de métodos virtuales, son los componentes que
más se acercan al
polimorfismo uniforme que
algunos lenguajes funcionales ofrecen
[Mil-84].
9.2 Direcciones futuras de investigación
Conviene mucho saber de dónde se ha partido al llegar al
final de la jornada de trabajo, pues en ese momento se puede
reflexionar sobre si se ha llegado adonde se quería, o a
otro lado. Esta investigación nació de dos preguntas
hechas por estudiantes. La primera es: "Profesor,
¿por qué no puso la lista en esa
operación?"
PROCEDURE Insert( p: ^Nodo; i: INTEGER ); VAR pN: ^Nodo; BEGIN NEW(pN); pN^.val := i; pN^.next := p^.next; p^.next := pN; END; { Insert } |
PROCEDURE Insert( VAR L: TList; p: ^Nodo; i: INTEGER ); VAR pN: ^Nodo; BEGIN NEW(pN); pN^.val := i; pN^.next := p^.next; p^.next := pN; END; { Insert } |
Insert(p,i)
vs
Insert(L,p,i)
Sucede que en la
Figura 9.1 lo correcto es no
omitir la variable "L
" del encabezado para la
operación List.Insert()
, que agrega un nuevo
valor a la lista en la posición "p
":
Insert(L,p,i)
Es irrelevante se se usa o no "L
" en alguna
implementación, pues toda
especificación debe
ser lo más general posible para permitir modificar y
mejorar la implementación. En este caso particular, la
especificación de la rutina incluye más
información que la necesaria para realizar una
implementación, que en este caso especial usa
punteros. Para contestarle su
pregunta al estudiante fue necesario
replantear su trabajo para finalmente obtener un módulo
ListC.pas
cuya
especificación fuera independiente de una
implementación específica
[DiM-94f].
Después de salvar ese primer escollo, otro alumno preguntó: "Profesor, ¿no tiene un programita para evitar usar el editor de textos cada vez que necesito una lista?" Los estudiantes, al trabajar en un proyecto que requería varias listas diferentes, y en particular una lista que contenía listas, comenzaron a necesitar apoyo automatizado para parametrizar sus listas. En lugar de cambiar el lenguaje, surgió la idea de parametrizar contenedores sin usar plantillas, o sea, parametrizar aun si el lenguaje está limitado en su sintaxis. De ahí nace el título de este trabajo: "Reutilización de Contenedores Parametrizables con Lenguajes de Semántica Limitada".
En esta investigación se logrado avanzar el estado del arte porque se han definido cuáles son los principios teóricos básicos necesarios para implementar la reutilización de componentes de programación, y luego se han usado para concretar una implementación reutilizable de contenedores parametrizables. Los resultados se han logrado buscando minimizar el uso de recursos, para al mismo tiempo aumentar la capacidad de reutilización de los componentes de programación. O sea, que se obtiene más funcionalidad con menos recursos.
El siguiente paso es trasladar estos resultados al lenguaje C++, para producir una biblioteca muy eficiente de módulos reutilizables que pueda ser distribuida como un servicio adicional del sistema operativo, posiblemente en la forma de módulos compilados de tipo DDL, para ser usada desde cualquier lenguaje que tenga soporte directo para programación orientada a los objetos (OOP). En particular, conviene que la misma biblioteca pueda ser invocada desde programas escritos en varios lenguajes diferentes, especialmente en Ada, C++, Pascal y Eiffel.
#define Link_to_PObj(link) (...) #ifndef NDEBUG // usa una función con prototipo bool InsertA(TList *L; TElem *e); #else // hace la invocación "inline" con una macro #define InsertA(L,e) \ InsertA_List(L, (TList_Tlink) Link_to_PObj((e)->link)) #endif |
Los primeros pasos en esta dirección ya se han dado, pues
el
autor ya ha comenzado a trasladar
los resultados obtenidos en este trabajo al lenguaje C
[DiM-99a] y
[DiM-99b]. En el segundo
artículo, titulado "C parametrized lists", se
muestra que la ideas desarrolladas al producir el módulo
Binder.pas
se pueden
transliterar con facilidad al lenguaje C, pese a que C no cuenta
con apoyo directo para
OOP.
Como se muestra en la
Figura 9.2 esta deficiencia de C
es suplida con el uso del preprocesador, pero logrando compartir
una única copia del código compilado.
A partir de los resultados obtenidos, también se puede lograr otras metas:
Binder.pas
, y la
de los principales contenedores, en lenguaje de
máquina.Binder.pas
se usaría como una herramienta de bajo nivel para una
biblioteca parametrizada sofisticada.Binder.pas
para que permita usar contenedores cuyo
campos de enlace son de longitud variable. Además, en C++
no se puede manejar campos de longitud variable con plantillas
[Mar-97].TYPECAST
usando
herencia múltiple. Si esto fuera cierto, se podría
usar como argumento a favor de incluir en los lenguajes de
programación la herencia múltiple, pues
sería el vehículo para implementar eficientemente
la parametrización de contenedores.Binder.pas
para aumentar la eficiencia. Una forma
interesante de lograrlo es usar metaprogramas de plantillas,
como los propuestos en
[VK-96], para maximizar la
eficiencia al instanciar el contenedor para un tipo de
elemento.Binder.pas
o, por lo menos,
una que sea más simple de usar.Binder.pas
ofrece tres servicios principales: ayuda
a definir qué es un campo de enlace, provee el ligamen
entre el contenedor y su elemento contenido, y provee los
procedimientos por defecto para los ADT's elementales.
Sería útil lograr que este último servicio
de Binder.pas
pudiera ser usado en contenedores que
no han sido implementados siguiendo el estilo de
programación que complementa a
Binder.pas
.Binder.pas
es heterogéneo, pero sería
muy provechoso lograr usar el sistema de
verificación fuerte de
tipos del compilador para asistir al programador cuando usa
este tipo de contenedor. Por ejemplo, en el campo de enlace se
puede incluir información sobre el tipo de dato
almacenado, su tamaño, etc.Binder.pas
a las necesidades de muchas otras
aplicaciones. Por ejemplo, es deseable, e instructivo, mejorar
la implementación parametrizable del ADT Árbol,
pues todavía hay que definir un acceso eficiente para el
k
-ésimo hijo, con
k>>1
. En el caso de la lista, es bueno
extender la implementación para que los nodos de enlace
también puedan contener un puntero hacia atrás,
para obtener una lista doblemente enlazada.Set.pas
(conjunto) y Map.pas
(diccionario), para facilitarle al estudiante el aprendizaje del
uso e implementación de Binder.pas
. Puede
que al hacer ese trabajo se concluya que es necesario incluir
Key()
entre las operaciones que por defecto
Binder.pas
ofrece, para usar la llave del valor
almacenado en el contenedor. Además, es importante
también disponer de un diccionario que almacene sus
valores usando una llave de dispersión (hash
key).Binder.pas
a veces es complicado, un uso que se le
puede dar a esta técnica es
utilizándola como una implementación de bajo nivel
para una biblioteca parametrizada como STL.
Muchos programadores nunca aprenden a usar contenedores en sus
programas; para ellos la parametrización será un
concepto complicado y, por ende, nunca podrán parametrizar
contenedores con Binder.pas
. Para otros, las ideas
usadas para construir Binder.pas
serán una
técnica más que sirve para implementar bibliotecas
parametrizables de alto nivel. En este caso,
Binder.pas
y sus módulos asociados
serán una biblioteca de bajo nivel. Tal vez otros
descubran cómo usar mejor Binder.pas
.
Es posible que algunos programadores nunca acepten implementar un
contenedor usando apuntadores entre los campos de enlace, que es
el estilo de programación que da sustento a
Binder.pas
. Pero tal vez esta resistencia sea
igual a la que experimentaron, en la década de los
años setentas, quienes se opusieron a la eliminación
de la instrucción GO TO
[Dj-75], pues después de
quejarse por algunos años, simplemente adoptaron el nuevo
estilo de programación sin mayor problema. El tiempo se
encargará de discernir qué es lo cierto.
¡Ojalá la parametrización de contenedores que se ha descrito en este trabajo llegue a ser, al menos, una fracción de lo útil que es un "Clip"!
Copyright © 1999 Adolfo Di Mare
|