You are on page 1of 420
ESTRUCTURAS DE DATOS Y METODOS ALGORITMICOS Ejercicios resueltos i inn CAPITULO ESPECIFICACION ALGEBRAICA DE TIPOS ABSTRACTOS DE DATOS BRAM ierere la Con ocasién del tltimo cambio de milenio en el calendario occidental tuvo lugar un fenémeno ge- neralizado de “histeria informatica” conocido como el problema del aio 2000, debido a la necesidad de adaptar equipos y programas informiticos para poder trabajar con fechas en las que el aiio se represen- ta con los cuatro digitos en vez de solamente con los dos iltimos. Por supuesto. como dijo el profesor Donald E. Knuth, el problema se agudizard cuando se acerque el afio 10000, pero en ambos casos, y sim- plificando muchas cosas, poxtemos decir que, desde el punto de vista de la programacién, se trata de un problema de in y el uso de un tipo de datos (en este caso, fechas). Cualquier cambio en la representacin de los datos, motivado por la limitacién de esta, nos obliga a buscar todos los lugares de los programas donde aparecen esos datos, para allf modificar la 1.1.1. Tipos abstractos de datos La idea fundamental de los tipos abstractos de datos (TADS) es separar de forma estricta la represen tacién del uso de los datos de un tipo. Para empezar hemos de tener en cuenta que un tipo no solamente consta de un conjunto de valores, sino también de un conjunto de operaciones para la creacién, modifica- cin y manipulacién de dichos valores, a través de las cuales se realizaré el uso de los datos. alta de abstraccién, al no diferenciar entre la represe' Por una pare, tenemos la representacidn del tipo de datos en términos de tipos basicos 0 de otros tipos yaconocidos, asf como la implementacién sobre dicha representacicn de las operaciones asociadas al tipo de datos, que constituyen la interfac de ese tipo, En los algoritmos que implementan esas operaciones se tiene pleno acceso a la representacién concreta del tipo de datos. Asi, en el ejemplo de las fechas se puede ver si el afto se representa con des 0 con cuatro digitos, 0 de otra forma. or otra parte, la tinica forma de usar los datos del tipo abstracto es invocando adecuadamente las operaciones de la interfaz, de manera que nunca se accede a su representacion interna, de ahi la abstrac- cidn, Asf podemos tener diferentes representaciones para un mismo tipo de datos, pero cuando se usa el tipo nunca se sabe cual es Ia representacién que se esti utilizando, Cuando se eambia la represen siempre y cuando la interfaz se mantenga, los programas que usan el tipo deben continuar funcionando exactamente de la misma forma que antes En particular, una vez se ha fijado la interfaz del tipo, la programacién de la representacién del tipo «© implementacién de las operaciones por un lado, y la programacién de los algoritmos que usan el tipo por otro, pueden proceder de manera completamente independiente la una de la otra. Sin embargo, para ue esto sea posible sin que los programadores de los segundos conozcan la representacién concreta 4 _Estructuras de datos y métodos algoritmicos _ que manejan los programadores de las primeras, hace falta que intercambien no solamente la interfaz sino también una descripcién apropiada del comportamiento de las operaciones que constitayen dicha interfaz, Estas ideas resultardn sin duda familiares para aquellos lectores que ya conozean la programacién orientada a objetos, que hist6ricamemte constiye un desarrollo posterior y que ha incorporado el con- cepto de abstraccidn estudiado para los tipos de datos, Entre muchas propuestas que existen para especificar el comportamiento de las operaciones sobre un. ‘ipo de datos, varios a considerar en este libro la conocida como especificacion algebraica 0 ecuacional, que se basa en describir el comportamiento mediante ecuaciones, lo cual faclita el estilo habitual de razonamiento ecuacional, basedo en sustituir iguales pox ales, See en ecu aes, n Un tipo abstracto de datos viene determinado por las operaciones asociadas, incluyendo constantes que se consideran como operaciones con cero argumentos. Entre estas operaciones vamos a distinguir unas que denominamos consiructoras (en algunos textos se las denomina generadoras) que sitven para construir 0 generar todos los datos det tipo que se esta especificando, Para un mismo tipo de dates, como veremos, puede haber distinias elecciones de cons- tructoras. Segsin las constructoras elegidas, puede ser que haya una tiniea forma de representar ean esas consirucioras cada dato, en euyo caso decimos que las constructoras son libres, 0 que haya diferentes represeniaciones para un mismo dato. En este tiltimo caso, es necesario identificar las razones por las que hay varias formas de decir lo mismo, proporcionando una serie de ecuaciones de equivalencia entre ‘érminos construidos. Las restantes operaciones de la interfuz del tipo de datos se clasitican en observadonas y modifiea- doras. Las primeras son aquellas que devuelven informacién sobre los datos que se estin especificando, y esta informacion pertenece a otf0s tipos de dates ya conocidos. Las segundas son operaciones cuyo resultado es un dato del mismo tipo que se especific El comportamiento de las operaciones observadoras y modificadoras se especifica mediante ecuacio- nes, que pueden ser condicionales. La metodologéa de constructoras consiste en que, una vez. elegidas las construcioras para el tipo de datos, las ecuaciones definen el comportamiento de las operaciones obser- vadoras y modificadoras en funciéa de su efecto sobre los rérminos canstruidos, 0 sea, sobre los que se definen sobre variables tipadas apropiadamente y las operaciones constructoras, Entonces, para wna operaciéa observadora 0 modificadora op cuyos datos de entrada tienen tipos 7) 73... Tp. las ecuaciones que definen su compertamieato tienen la forma OPEC. C24 2. ly EHS AAW F donde los términos ¢; son téiminos construids de tipo 7; para i entre | y m, mientras que fy ¥ 1.5 (con j entre | y A, y k = 0) son términos que ademas de las constructoras pueden involucrar también la propia operacin op (dando lugar a definiciones de cardcter recursive) y otras operaciones modificadoms wobservadorss. Nétese que la condici6n de la ecuscién anterior es una conjuncién (que puede ser vacia) de ecuacio- res. Dado un predicado P, es decir, una operacién con perfil P : T, T2...Tn —* boot, donde boo! es ¢l tipo usual de los valores booleanos (véase la Seceién 1.1.4), abreviaremos una eondicién de la for- ma P(ti,..-tn) = Gerto como P(ti,....tn). ¥ una condicién de la forma P(N... f») = falso como P(i),..., f,), donde — denota la negacidn sobre booleanos. Mas en general, una ecuacién de la forma 1 = olerto, donde r es un término de tipo book, se abrevia como ren las condiciones. Las operaciones que constituyen la interfaz de un tipo de datos, ineluyendo lay constructoras, pueden ser parciales. Esta situaci6n se explicitaré en la especificacién sefalando por un lado las operaciones parciales (mediante un subindice p en el perfil de la operacién), y escribiendo por otro lado ecuaciones de error que indican en qué situacin la operacién en cuestién no esti definida, Sin embargo, no haremos Especticacién algebraica de tipos abstractos do dalos_ 5 ‘un tratamiento explicito de errores, es decir, que vamos a suponer fmplicitamente que cualquier opera ccidn aplicada a un error devuelve un error, ¥ no vamos a escribir ecuaciones que hagan explicita esta propagacisn de errores. eel Presentamos a continuacidn un esquema de especificacién de tipos abstractos de datos para intioducir la notacién que vamos a seguir en este libro. Se trata de un lenguaje abstracto de especificacién que n0 corresponds exactamente a ningen leaguaje concreto de especiticaciOn entre los muchos propuestes en la literatura sobre este tema. si bien guarda cierto parecido general eon algunos de ellos especiticacién NOMBRE-ESPEC usa BOOLEANOS. OTRA-ESPEC, OTRA-MAS tipos nomore-tioo peraciones © nombrestipo 1: nombre-tipo ... nombre-tipo, nombre-tipo @ : nombre-too, ....nombre-tip0,, —> » otro-tipo operaciones privadas P= nombre-tioo ... nombre-tipo, bool h : nombre-too, .. . nombre-tipo, atro-tipo variables x. : pombre-tipo =: otrotipo ecuaciones En primer lugar, tenemos la signetura o interfas del tipo de dates, que consta de © el nombre de la especificacién, © una posible importacién de otras especiticaciones de tipos dando sus nombres, © ladeclaracién de! nombre nombre-tipo del tipo de datos que se define, que en casi todos los ejemplos, 5 solamente uno pero en general pueden ser varios, ¢ a dectaracion de las operaciones asociadas, eas una de ellas dectarada con su perfil, es decir, fos tipos de los datos de entrada y el tipo de! resultado, Enel exquema anterior, 6 es una constante pues no tiene argumentes, {es una operacién total y ges tuna operacién parcial como indica el subiadice p junto a la flecha det pectl. Las dos primeras podsian ser cconstructoras 0 moditficadoras, mientras que la teivera.es una observadora pues su resultado tiene un tipo iferente al especificado. Después de las operaciones de la interfaz aparece la declaraci6n de operaciones privadas ‘que son iitiles en Io especificacién pero no se pueden usar fuera de 1a misma. En el esquems ejemplo tenemos un prediesdo p (pues el tipo de su resultado es boo!) declaradlo como operacién privada, junto a ‘ou operaciGn privada h. Cuando sea conveniente © habitual, utilizaremos notacién infija para las operaciones; en tal ease tusaremios el signo de subrayado para indicar las posiciones de los argumentos en la declaracién de la ‘operacién, como por ejemplo _-+_ para una operacién binaria de suma, “Tras la signatura viene ladeclaraci6n de variables, cada una con su tipo correspondiente, que se usarin ‘en os términos para formar ecuaciones; y finalmente, la lista de ecuaciones, posiblemente condicionales En el esquema ejemplo tenemos una ecuaci6n sin condicida y otra condicional 6 _Estructuras de datos y métodos algeritmicas Recordemos que las ecuaciones se pueden clasificar, tal como hemos explicado anteriormente, en: ‘© ecuciones de equivalencia entre términos construidos, cuando las constructoras no son libres, '® ecuaciones de error, en caso de operaciones parciales, y ‘© ecuaciones que definen el comportamiento de las operaciones modi funcidn de las constnuctoras. \doras y observadoras en SRee ee eee eee Debido a su sencillez y a ser bien conocido tantoen el contexto de la légica como en el de los lenguajes de programacién, consideraremos como primer ejemplo de especificacién algebraica el tipo de datos de os valores booleanos. especificacién BOOLEANOS ips boo! operaciones Gerto boot { constnuctora } {also + bool { constructora } = a _A.. + bool boo! —> boo! V_ + boalbeot —> bool ‘Tenemos constantes certo y faso, la operacién unaria de negacién y operaciones binarias de conjun- ign y disyuneién, Puesto que el tipo del resultado de todas las operaciones es boo! que es el tipo que se especifica, en este ejemplo no hay operaciones observadoras, Entre las operaciones dadas en la interfaz, tenemos que elegir un conjunto de constructoras. Las das constantes obviamente representan de forma tinica los dos valores del tipo de datos que se define, por lo que consituyen un conjunto de constructoras apropiad, tal como se ha seftalado en 1a Signatur ant Al tratarse de dos constantes, el conjunto de téminos que definen esta constituido por esas dos mismas constantes, y como cada valor esté representado exactamente por una de ellas, n0 hacen falta ecuaciones de equivalencia entre los términos construidos, es decir, las constructoras son fibres Con esta eleccidn de construcioras (que no es la nica posible, como veremos en el Ejercicio 1.1), las otras tres operaciones son modificadoras y dehemos definie su comportamiento en términos de las constructoras. Para ello consideramos, en primer lugar, la operacién de conjuncién, que es binaria, con dos argumentos de tipo bool. Como hay dos constructoras del tipo bool, en principio para cada uno de los argumentos tenemos dos posibilidades, eon lo cual serfa posible considerar cuatro ecuaciones de la forma siguiente: cierto A certo = cierto cierto A falso = also {also / cierto falso falso/ faiso = falso que representan la tabla de verdad de la conjuncin. Sin embargo, no es necesario distinguir tantos casos {cuando ef nimero de constructonas se incrementa, este detalle adquiere més importancia a la hora de facilitar la lectura y comprensién de la especificacién), pues basta con tener informacin sobre un argu- mento para saber el resultado, utlizando una variable & de tipo boo! pam representar de forma genér el otto argument: ciertob = b falsoAb = falso Especiticacién algebraic de tipos abstractos de datos_7 En exte ejemplo da igual el argumento sobre el cual empiezan a dist portamiento de ta conjuncién es simétrico con respecto a los dos argumentos, es decir, se trata de una ‘operecién binaria conmutativa, Sin embargo, aunque esta propiedad sea cierta, no vamos a poner en la una ecuaci6n de conmutatividad de la forma guirse Tos casos, pues el com- bab ‘que no sigue la metodologia de constructoras, pues no define el comportamiento de fa operacién que se ‘esti especificando sobre valores del tipo de datos (dados como términes construidos). Razonando con las ‘dos ecuaciones anceriores, se puede ver que la ecuaci6n de conmutatividad es de hecho convecuencia de esas dos ecuaciones y es. en ese sentido, innecesaria en esta clase de especificaciones. Las ecuaciones que especitican el comportamiento de ta disyuncién son pareeidas a las de ta con- jimcién y las de la negacién son inmediatas, de forma que el esto dela especificacién tiene la siguiente forma, dende las ecuasiones aparecen tras fa declaracion de ls variables ie se usin: variables b: boo! ecuacianes. “cierto. = ‘also falso cierto certonb = b falco Ab falzo certovb = cierto falso vb b fespecificacién n esta especiticacién no hay ninguna ecuacién condicional. Otra caracteristica habitual de las esp citicaciones ecuacionales, que tampoco se aprecia en este ejemplo debido a su sencillez, es la definiciGn recursivade las operaciones, como veremos en la mayorta de los ¢jercieios que siguen igual Muchas de las especificaciones de tipos de datos habituales que Iremos viendo en los ejercicios y los capitulos siguientes, como son pilas, colas, istas, conjuntos, etc., son genéricas 0 paraméricas con respecto a los dates que contienen, es decir, la especificaci6n de fa construccién genérica de datos que se define es completamente independiente de los elementos concretos que constituyen esos datos. En tales ‘casos, Ia especificacidn esté parametrizada con respecte a un pariimetro que indica los requisites que debea cumplir los datos coneretos para que tenga sentido aplicarles la construcci6n paramétrica, Aunque la estructura de un parimetto es similar a la de una especiticacién, y dentro de esta se usa como una especificacién importada, el parémetro debe entenderse como wn argumento formal de la espe- cificaci6n que se va a instanciar adccuadamente cuando se use el tipo de datos genético. Por ejemplo, una 1€7 definidos los conjuntos en general, después podemos usar los conjuntos de booleanos, Jos conjuntos de naturales, los conjuntos de conjuntos de naturales. etc EI primer ejemplo de purdmetro que consideramos solamente exige la existencia de un tipo. pardmetro ELEM tipos efemento fparimetro Aunque esto puede ser suficiente en algunas situaciones, en muchos casos interesa que el tipo tenga una operacidn de igueldad que permita comparar elementos entre si por ejemplo, si queremos poder decidir si dos conjuntos son iguates, debemos poder comparar los elementos que los forman para ver si aa You have either reached a page that is unavailable for viewing or reached your viewing limit for this book. aa You have either reached a page that is unavailable for viewing or reached your viewing limit for this book. 10 _Estructuras de datos y métodos algoritmicos representar de miltiples formas, hay que asegurarse que las ecuaciones que definen lay restantes opera cciones (todas ellas modificadoras) son independientes del representante elegido. Volvemos a escribir la signatura completa de Ia especificacién, haciendo notar que el tinico cambio es Ja elecei6n de constructoras, y a continuaciGn incluimos el axioma de equivalencia y la definiciin de la ‘constante falS0. especificacién BOOLEANOS+2 tipos boo! ‘operaciones cierto > bool {constructors } falso — boo! = bool = —+ bool _{ constructora ) -A- : bodtboo! —> bool V+ bool Boot —> boo! => _ + boolboot —> bool = 2 Boal Boo! —> boo! —xor_ + bool boot —> boo! variables b,c: boot ab = b (doble negacién } falso = “cierto Para especiticar la eonjuncicn y la disyuncicn, distinguimos casos sobre constructoras en el primer argumento, Cuando el primer argumento es cert, tenemos sucieate informacion para conocer el resul- tado directamente, pero cuando es una negacidn, hemos de cistinguir casos en el segundo argumento, ‘Cuando el segundo argumento es cierto, damos el resultado directamente, y cuando es oira negacién, nos apoyamos en las conoeidas eyes de De Morgun que afiman que la conjuncidwvdisyuneién de negaciones es equivalente ala negaci6n de la disyunci6n/conjuncisn, con lo que se reduce la conjunci6n inicial auna_ disyuncién mas sencilla, y viceversa, De esta forma obtenemos una definicién mutuamente recursiva de _ambas operaciones. La recursién esti bien definida porque el tamato de los correspondientes argumentos decrece estrictamente: concretamente, en la tercera ecuacién de la conjuncién y de la dis mentos en la izquienda son los témiinos ~b y -c, mientras que en la derecha se reducen a los subiérminos, bye. cierto Ae c —b cierto = —b bac ave) cierto ve cierto hv cierto = cierto abv Alb Ac) Pars las tres Ultimas operaciones hacemos la misma distincién de casos, dando lugar a definiciones recursivas, pero no mutuamente recursivas. cierto => « ¢ “b= certo = certo bare = sb cietose = © —b = certo 12. Especificacion algebraica de tipos abstractos de datos 11 cierto xor c ~ sb xor cierto = b sb x01 7c b xore ‘Tanto en este apartado como en el anterior, en vez de distinguir casos sobre constructoras, podemos también escribir ecuaciones que definen unas operaciones l6gicas en funcién de otras, de acuerdo con conocidas equivalencias Kogicas. Por ejemplo, bac = Cb)ve bac = b> 0NC>b) b x0 6 = b=0) fespecificacién Especificar los mimeros naturales con las siguientes operaciones: © cero y sucesor, producto, diferencia de naturales (al restar a un ntimero otro mayor el resultado que se obtiene es cero), potencia, relaciones de igualdad y desigualdad, relaciones de orden <, <, > y > @ cociente y resto de la division entera, y # predicades para reconocer si un natural es par © impar, Solucion: La eleccién de constructoras no admite muchas posibilidades, pues con las operaciones dadas en el cenunciado la tinica forma de generar un ntimero natural es aplicar sucesivas veces la operacién sucesor suc (que intuitivamente sumauno) a laconstante cero. Mas coneretamente, el nlimero n se representa de forma univoca mediante el término construido suc” (cero) donde la notacién suc” indica que la operacién suc se aplica n veces. En definitiva, tenemos una representaci6n unaria (es decir, en base 1) de los nimeros naturales, de forma que para obtener el nsimero n empezamos con el cero y sumamos uno n veces. Como la representaci6n de cada valor del tipo definido es tinica, no hay ecuaciones de equivalencia entre los términos construidos, es decir, las constructoras cero y suc son libres A continuacién vemos la interfaz de todas las operaciones que se piden en el enunciado, Notese que los predicados de orden y de paridad son observadoras, pues el tipo de su resultado es boo! y todas las dems, excepto las dos constructoras, son modificadoras, Por otra parte, todas las operaciones son totales, excepto div y mod que son parciales, debido a que no se puede dividir por 0. especificacién NATURALES usa BOOLEANOS tipos nat operaciones cero — nat { constructora } suc rat nat ( constructora } _+_ : natnat —> nat * rat nat —> nat nat nat —> nat rat nat —> nat 12__Estructuras de datos y métodos algoriticas ===: natnat —- — boot natnat —> — bool ratnat —+ — bool natnat —> — doo! natnat —+ — bool natnat —> — bool natnat —> nat in : natnat —+ nat dv: natnat —+y nat “mod_ : natnat —> nat espa? : nat = —> boot esimpar? : nat —+ — bool variables nym : nat Ahora debemos especiticar el comportamiento de las operaciones observadoras y modificadoras en términos de las constructoras. Empezamos por la suma, en cuya definicién podemos aplicar las mismas ideas que ya vimos en ta Seccién 1.1.4 para la definicién de la conjuncién sobre tos booleanos, En principio, dado que tenemos dos constructoras del tipo nat, podemos considerar dos posibilidades para cada uno de los argumentos de la suma, dando lugar a cuatro ecuaciones con casos cero-cero, cero- sucesor, sucesor-cero y sucesorsucesor. Sin embargo, la especificacién se puede simplificar reduciendo e! mimero de ecuaciones al tener en cuenta que basta informacidn sobre Ia consiructora en uno de los ‘argumentos para saber como realizar la suma; por ejemplo, si un argumento es cero, el resultado coincide con el otro argumento. Como la suma es conmutativa, es equivalente realizar la distincién de casos en uno u otro argumento xy aqui lo hacemos sobre el primero. Si es cero, el resultado coincide con el segundo argumento, como ya hemos comentado. Si el primer argumento es un sucesor tenemos una definicién recursiva, haciendo uso de fa igualdad matemitica (n + 1) +m = (n +m) + I. La recursi6n esté bien definida porque el tam: {del primer argumento de la suma decrece estrictamente en la Hamada recursiva, pasando de ser suc) aser a, ecuaciones, cero +m m suo(n) +m = suc(a +m) Intuitivamente, teniendo en cuenta la notacién unaria, la interpretacién recursiva de las dos ecuaciones anteriores se basa en que sumar 7 aim es lo mismo que sumar n Yeces uno a m. Como ya hemos comentado para la conjuncién booleana, no consideramos una ecuacién de conmu- tatividad para la suman +m = mt + 1, porque por una parte es una consecuencia de las dos ecuaciones teriores. y por otra parte no sigue la metodologia de constractoras que nos interesa Las consideraciones que acabamos de hacer para la distineién de casos en ka especificacién de la suma se aplican también al producto de niimeros naturales: hastan des ecuaciones en vez de cuatro y no nporta distinguir casos en uno u otro argumento por la conmuiatividad. Cuando el primer argument 2s cor0, el resultado es también coro. Cuando el primer argumento es un sucesor, se hace una Hamada recursiva, utilizando la igualdad matematica (1 + 1) + m = (nx m) + m. Obsérvese que utilizamos la suma, definida anteriormente, en la definicién del producto, La recursiGn esti bien definida por la misma raz6n que antes ceroem = cero ucts) * m (nem) tm Intuitivamente, la interpretaciGn recursiva de estas dos ecuaciones se basa en que multiplicar n por m ¢5 Io mismo que sumar veces la cantidad m. Especificacion algebraica de tipos abstractos de datos 13 ‘Con la resta la sitaaci6n es algo diferente, al no ser una operaci de casos no es simétrica con respecto a los dos argumentos. En las ecuaciones que siguen distinguimos primero casos en el primer argumento y cuando este es un sucesor distinguimos casos en el segundo. Ei las dos primeras ecuaciones, e1 resultado es inmediato, mientras que en Ta tercera se hace una Hamada indo la igualdad matematiea (n + 1) — n+ 1) n commutativa, porlo que la distinci6n recursiva, wil cero ~ ma cero. sucin) ~ ce suc() suet) ~ sucim) = nm se basa en el producto, de Ia misma forma que el producto se basa en fa suma en el segundo argumento, el exponente, en vez. del La exponenciaci pero es importante que la disiincidn de casos se h primero, que es la base. expin. cero) suc(cero) expin, suctmn)) = nm exp(n.m) La especificaci6n del predicato de igualdad necesita distinguir los cuatro casos que se obtienen al considerar las dos constractoras para cada uno de los dos argumentos. Las tres primera ectaciones pro- porcionan un resultado directumente, mientras que la euarts es recursiva, Para la desigualdad podriamos dlistinguir los mismos cuatro casos, pero claramente es mucho més sencillo defini Ia desigualdad como la negacién de la igualdad, mediante una tinica ecuacién coro == cero cierio cero == suc(m) falso suctit) false sucin) nem mn) La distincién de casos para la operacién < es exactamente la misma que para la resta, Mediante distinciones de casos semiejantes se podria definir cada una de las restantes relaciones de orden de forma independiente, pero en la especificacién que sigue detinimos < en téminos de msmem amen La distincién de casos para el maximo y el minimo vuelve a ser la misma que para < y la resta, si bien, en este caso, las operaciones son conmutativas. méxicoro, ) m max(sucirt),cero) = sve(n) max(suci).sucin)) = suc max(n, m)) min(cero, m) = ceo min(suc(), cero) min(suc(), suctin)) suc(min(n.m)) Para las operaciones div y mod la distincién de casos no esté bass nstructoras, sino en la relacién de orden entre los argumentes, y por esta razdn se hace mediante jones ecuacionales, En 14 _Estructuras de datos y métodos algoritmicos 13. primer lugar tenemos una ecuacién de error debido a la parcialidad, En segundo lugar el caso bisico es aquel en que el dividendo es mas pequefio que el divisor, en euyo caso el cociente vale cero y el resto coincide con el dividendo. Finalmente, cuandoel dividendo es suficientemente grande, la tercera ecuaciGn produce el resultado a partir de una llamada recursiva basada en la resta; intuitivamente, la divisién se reduce a sucesivas restas, de la misma forma que multiplicar significa realizar sucesivas sumas. ndivcem = error ndivm coro = nm ndwm — = suc((n=m)divm) = m #cero Am complejo { constructora } 2 complejo = ent m complejo — ont sumac: complejo complejo —> complejo restec —:_compleja complejo complejo prods complejo complejo complejo oni complejo — complejo _==_ complejo complejo —» boo! val compreyo en variables a,b,c. ent ‘ecuaciones retee(a.b)) = a im(cota.b)) = 6 sumacteri(a, b). cote. d)) = eclat+e.b+d) restec(co(a. b).ce(c.d)) = cola —e.b—d) prode(ecta. b), cc(e. d)) = cela e) — (bd), (be) + (aed) conifecta, b)} = co(a, cambio-signo(b)) cola, b) == cole, d) = a==b A € val(oc(a,b)) = (axa) + (bb) fespecificacion Notese que para la relacién de igualdad sobre los complejos estamos utilizando la misma notacign que para la relacién de igualdad sobre los enteros, por fo que en Ia ecuacién que define 1a igualdad de complejos el sfmbolo == aparece sobrecargado, Sin embargo, el contexto permite desambiguar sin dificultad alguna: en la parte izquierda de la ecuacién el simbolo denota la igualdad sobre los complejos, mientras que en l: a la igualdad sobre enteros, arte derecha de la misma se refiere en ambos caso Por otra parte, aunque la ecuacién val(c} = prode(c, conj(e)) es mateméticamente correcta para defi el valor absoluto de un niimero complejo como el producto de ese niimero por su conjugado, no sirve en la especiticacién porque la funci6n val tiene que devolver un ndmero entero (no negativo), mientras que el término prodc(c, conj(c)) es un producto de complejon y es, por tanto, un término cuyo tipo es complejo cen vez de ent. Una forma correcta es escribir val(c) = re(proda(c. coni(c))}. 18 _Estructuras de datos y métodos algoritmicos 15. Solucién: ‘La especificacion de las cadenas es genérica 0 parameétrica con respecto al alfabeto sobre el cual se construyen las cadenas, es decir. la especificacién es independiente de los elementos concretos que cconstituyan el alfabeto en un uso particular de las cadenas. Por esta raz6n, tendremos una especificacin ‘parametrizada con respecto a un pardmetro que proporciona los requisitos que debe satisfacer dicho alfabeto. ‘Como algunas de las operaciones que debe incluirla especificacién necesitan comparar clementos del alfabeto (por ejemplo para ver si un elemento aparece en una cadena o si dos cadenas son iguales). nos hhace falta una operaci6n de igualdad sobre el alfabeto. Asi, el parémetro que vamos a utilizar es ELEM=, definido en la Secci6n 1.1.5. que exige como requisitos un tipo denominado elemento (que representa el alfabeto) y una operacién de igualdad denotada == (esta notacién estard sobrecargada porque vamos a ‘denotar de la misma forma la igualdad sobre cadenas). La siguiente signatura define la interiaz de la especificacién, que es comiin a las tres versiones de la misma, Nétese que las cuatro operaciones para consultar y eliminar el elemento més a la izquienta y ala derecha de una cadena son parciales porque no estén definidas sobre la cadena vacta, ‘especificacién CADENAS[ELEM=] usa BOOLEANOS, NATURALES tipos cadena operaciones cadvacia : — cadena anizg elemento cadena —> cadena aii-der cadena elemento —> cadena unit elemento — cadena concat cadena cadena —> cadena Copyrighted material Especticacién algebraica de tipos abstracios de datos 19 tongitud 2 cadena — net Prim-ieg cadena elemento ime cadena Sp cadena stim-der cadena +» elemento elim-der cadena +p cadena es-cadwvacia? : cadena +" boot est? © elemento cadena —> — beol — : cadena cadena —> — bool inversa 2 eadena — cadena variables . f elemento Xx, Yet cadena Apartado (a) ‘Cualquier cadena se puede construir de forma tinica a partir de la cadena vacfa afadiendo sucesiva- mente sus elementos por la izquierda. Por tanto, cad-vacia y af-iaq constituyen un conjanto de construc- toras libres, y no hay ecuaciones de equivalencia entre téminos construidos, Las restantes operaciones las definimos, en general, distinguiendo casos sobre las dos constructoras del tipo cadena. Conviene hacer notar que no tiene sentido distinguir casos sobre constructoras para argu- mentos de tipo elemento, puesto que este tipo “variable” viene del parimeiro y no tenemos constructoras para el mismo, Cuando se utilicen las cadenas sobre un tipo eoncreto, el tipo elemento se instanciaré ade- cuadamente, pero en Ia especificacidn genérica de las eadenas esa informacidn ni se tiene nies necesaria, La cadena unitaria se define inmediatamente en téeminos de las constructoras, mientras que para Las operaciones af-der y longitud distinguimos los dos casos en el argumento de tipo cadena, haciéndose las amadas recursivas apropiadas en el caso no vacio, La operacidn de concatenacion es binaria, pero no conmutativa, y es mas conveniente distinguir los casos en un argumento o en el otro, segtin Ia eleccisn de constructoras. En este apartado la distincién se hace en el primer argumento, porque aftadir por la izquierda tiene que ver con el “principio” de la cadena. ecuaciones afi-der(catl-vacia, f) afizg( f, cad-vacia) ai-der(aiizgte,x). f) = alvizg(e, al-destx, f)) wnit(e) = afizg(c, cad-vacia) concat(cad-vacia. ») = y concat(arrizg(e,.x),.y) = af-izgle, concat(x, ¥)) tongitudcad-vacia} 0 Jongitud(af-izq(e,.x)) = 1 + longitud(x) Las operaciones para consultary eliminar el elemento més a laizquierda de una cadena dan error sobre In cadena vacia y son las proy pues podriamos Hamarlas las “destructoras” ssociadas a la constructors afi2q prim-i2q(cad-vacia) prim-ieq(efizq(e,.x)) error dlimizq(ead-vacia) = orrer elim-ieq(afvizg(e,x)) = 3x En cambio, las operaciones para consultar y eliminar el elemento mas a la derecha descomponen una cadena en componentes que no son Jos argumentos de la constructora afi y, por tanto, su definicién es mas complicada, necesitandose distinguir mais subcasos dentro del easo no vacio, Si la cadena es unitaria, 20 _Estructuras de dates y métodos algoritmicos cel elemento més a la dereehs coincide con el ms. a la izquierda (esl tinico que hay en 1a cadena), y si no es unitaria, recursivamente se busca en la parte x de Ja derecha quitando el elemento més a Ta izquierda e. Para la operacidn de eliminar, la distincign de casos es ta misma, pero hay que tener cuidado en ta Hamada recursiva, ya que si solamente escribigramos a la derecha de la ecuaci6n el término elim-der(x) es! eliminando tanto el elemento més a la derecha de la cadena original como el de mvs a la izquierda e. prim-der(cad-vacia) error prim-der(arizg(e, cad-vacia)) = ¢ prim-der(aivizg(e, x) primder(x) <= —es-cad-vacia?(x) elim-der(cad-vacia) = error elim-der(anizq(e, cad-vacia)) = cad-vacta elim-der(anizg(e, x)) atiza(e. elim-der(x)) <= —es-cac-vacia?(x) Los predicados para comprobar si una cadena es vacfa y para ver si un elemento aparece en una cadena tienen una definicion recursiva inmediata, utilizando [a igualdad entre elementos en la definicion del segundo. es-cad-vacia?(cad-vacia) = cierto es-cad-acia?(afvizg(e,.x)) = falso est8?( f, cad-vacia) esté?( f, afvizate. x)) ev esta fx) Laespecificacicn de ta igualdad entre cadenas necesita distinguir los dos casos en los dos argumentos, dando lugar a cuatro ecuaciones. Las tres primeras tienen resultados inmediatos, mientras que la cuarta realiza una llamada recursiva. Nétese la sobrecarga del simbolo ==: en la pante izquierda de las cuatro ecuaciones denota [a igualdad sobre cadenas, mientras que en la parte derecha de la cuarta ecuacién J compara elementos y x == y compara cadenas, dando lugar a la llamada reeursiva, = cierto cad-vacia falso afvizg(e,.x) ==cadvacia = also afvizgte. x) aieg fy) = e==f x Finalmente, para caleular Ia inversa se distinguen casos, realizéndose una Hamada recursiva en el caso no vacio, que hace uso de la simetria entre afiadir porla izquierda y afd por la derecha lnversalcadvacia) = cad-vacia inversalaf-izq(e, x)) af-der(inversa(x), ¢) fespecificacion Apartado (b) ‘Cualquier cadena se puede construir de forma tinica anadiendo sucesivamente por la derecha a partir de la cadena varia, Por tanto, cad-vacia y af-der también constituyen un conjunto de constructoras libres. La situaci6n es completamente siméiica a la del Apartado (a) y las ecuaciones que siguen se hasan esencialmente en las mismas ideas que las anteriores, teniendo en cuenta dicha simetria: ahora conviene distinguir casos en el segundo argumento para la concatenacién, las operaciones para consulta y eliminar el clemento més «la derecha de una cadena son las “destructoras” asceiadas a la constructora af-der, fa especiticacién de las operaciones para consultar y eliminar el elemento mas a la izquienda de una cadena esalgo mas complicada y se basa en distinguir tres casos, e inversa utiliza la simetrfa entre afiadir por Ia derecha y attadir por Ta izquienda. aa You have either reached a page that is unavailable for viewing or reached your viewing limit for this book. 22 _Estructuras de datos y métodos algoritmicos afvizq(e,x) = concat(unit(e), x) afv-der(x.e) = coneat(r, urit(e)) La longitud se define distinguiendo los tres casos posibles para las constructoras. Notemos que la definicién no depende del representante elegido: por ejemplo. si concat(x, y) = concatix’, y'). entonces longitud(x) + longitud(y) = longitud(x’) + longitud(y’) aunque tengamos longitud(x) # longitud(x’) y longitud( y) # longitud(y’). longitud(cad-vacia) 0 longitud(unit(e)) 1 longitud(coreat(x, y)) = longtud(x) + longitudty) Las cuatro operaciones para consultar y eliminar el elemento mas a la izquierda y a ja derecha de una cadena dan error sobre la cadena vaeta, pero ahora hay infinitos términos que representan dicha caden: ademas de la constante cad-vacia, por lo que la ecuacién que define la situacién de error es condicional, utilizando el predicado es-cad-vacia? (definido més abajo) para que Ia ecuacién se aplique a cualquier representacién de la cadena vaca Las restantes ecuaciones para cada una de estas operaciones se basan en distinguir casos sobre las dos constructoras restantes, y en el caso de la concatenaci6n distinguir a su vez subcasos para ver qué parte no es vacia, El subcaso en el cual ambas partes son vacfas no aparece, porque entonces la cadena total es asimismo vacfa, y esta situaci6n ya ha sido considerada en la ecuacién de error. error <= es-cad-vacia?(.1) primizgix) <= -es-cad-vacia?(x) prinizg(y) <= es-cad-vacia?(x) -es-cad-vacia?(y) prim-iza(x) prim-iza(unit(e)) prim-izq(coreatix, ¥)) primizq(corcatix, y)) celimizg(x) elim-tzq(unit(e)) ‘lim-izg(concat(r, y)) elim-izq(coreat(r, y)) error <= es-cadwacia?(x) cad-vacta concat(alimiag(x), y) €= es-cad-vacia?(x) alimizq(y) + es-cad-vacia%(x) ~es-cad-ve ia?(yh prim-der(x) prim-der(unit(e)) prim-der(concatx, »)) prim-der(coneatx. 5) elim-dertx) elim-der(unit(e)) elim-der(concatix. y)) elim-der(concat(x. »)) errcr <= es-cad-vacia?(x) e prim-der(y) <= —es-cad-vacia?(y) prim-der(x) <= es-cad-vacia?(y) A —es-cad-vacia?(r) error <= es-cad-vacia?(x) cadwvacia coneat(x, elim-der(y)) <= —as-cad-vacia(y) elimder(x) <= es-cadvacia%(y) A ses-cad-vacia?(x) La especifivaciGn de los predicados para comprobar si una cadena es vacfa y para ver siun elemento aparece en una cadena se basa en distinguir los tres casos de las constructoras y combinar adecuadamente las Hlamadas recursivas sobre las pares en el caso de la concatenacién, de forma ansloga a como se cn Ia especificacion de longitu, es-cad-vacia?(cad-vacia) cierto es-cad-vacia? (unit()) = falso es-cad-vacia?(concat(x. y)) = es-cadvacia?(r) ” es-cad-vacia?(y) esti7(e, cadvacia) = also esta?(e, unit f)) e=n esté?(e, concat(r, y)) = estd?(e,x) v esta%e, y) Para comprobar la igualdad entre dos cadenas, en general no sirve la descomposicidn que proporci nan las constructoras porque, por ejemplo, podemos tener concat(x, ¥) = concat(x’, y’) aunque x = 2” aa You have either reached a page that is unavailable for viewing or reached your viewing limit for this book. 24 _Estructuras de datos y métodos algoritmicas rote-izq(cad-vacia) cad-vacia rota-izqiafviag(e..x)) = af-derix,e) En cambio, para la operacién rota-der necesitamos acceder al elemento més a la derecha, para mo- verlo al extremio izquierdo. Como la constructora af-izg no proporciona esta descomposicién, usamos las ‘destructoras” prim-der y elim-der, en vez de distinguir casos por constructoras, rota-der(cad-vacia) = cad-vacia rota-der(x) afvizg(prim-der(x), elim-der(x)) ¢= —es-cad-vacia?(s) La operacién F-esimo se define recursivamente sobre el indice dado como segundo argumento. Aparte de la situacién de error ya comentada antes, el caso bisico es aquel en el que el indice vale |, y por tanto el elemento i-ésimo coincide con el mas a la izquierda, Fl caso recursivo reduce n a — 1, quitando el elemento mis a la izquierda de la cadena, i-ésimo(x, n) ésimo(an-iza(e, x), 1) ésimolan-ieq(c,.x).m) = error =n Vn > longited(xy imo(x.n— 1) = n> An— << lengitud(x) (Otra distincion de casos equivalente es la siguiente: i-6simo(cad-vacia, 1) error besimolanieg(e,.r).0) = error bésimo(anieg(e,x),1) = @ iésimotatieg(e, x),n) = Fésimo(x,n — 1) = n> 1 La operacién medio es un caso particular de -ésimo, en el sentido de que medio(x) =i-ésimotx. (longitud(r) +1) div 2) Pero también la podemos definir de forma independiente, distinguiendo los dos casos sobre cons- tructoras y dentro del segundo los tres subcasos correspondientes a cuando la cadena tiene 1, 2 0 mis elementos. En el timo subcaso se hace wna llamada recursiva, eliminando un elemento en cada extremo de la cadena original. medio(cad-vacia) = error medio(aA-izq(e, cad-vacia)) e izq(f.cad-vacia))) = © faq(f.x))) medio(af-izq(f, elim-der(x))) <= es-cad-vacia?(x) fespecificacién Apartado (b) Ahora ta situaci6n es simétrica con respecto al Apartado (a). La descomposicién de una cadena no ‘yacia proporcionada por la constructora af-der es apropiada para la operacion rota-der, mientras que para Ja opericién rota-izg usamos las “destructoras” primsizg y olim.izq, ‘ecuaciones rota-za(cadvacia) = cad-vacia rota-iza(x) = afder(eimizg(), prm-2q(s)) & ~es-cad-vacia?(x) rota-der(cad-vacia) cad-vacia rota-der(afder(x,e}) = afviagle.x) Laecuacién de error en la definicidn de i-ésimo es la misma de antes, pero en esta ocasién la recursién es diferente, Cuando el indice, dado como segundo argumento, coincide con la longitud de la cadena Especiticacion algebraica de tipos abstractos de datos 25 1. dada como primer arzumento, cl resultado es el elemento més a la derecha de la cadena, proporcionado directamente por la descomposicién de la constructora af-der. Si el indice es mas pequefio, quitamos ese elemento en la cadena y hacemos wna llamada recursiva, sin cambiar el valor del indice. irésimo(x, n) error <= n n> longitud(x) ésimo(afider(x.e),n) = ¢ = n==longtud(r) +1 simolaf-der(x.e),n) = i6simo(v.n) = 0 longiud(x) i-ésimo(x, 1) Prim-izg(x) <= —es-cad-vacia(x) i-ésimotx.n) i-ésimo(elin-izq(x), 1 — 1) = nes-cad-vaciat(x) 4 0- 2 fespeci Especificar los conjuntos finitos sobre un tipo de elementes dacto eo 9 parimetro. La especiticacion debe incluir al menos las fentes operaciones: © crear un conjunto vacfo, ‘* formar un conjunto unitario con un elemento dado, © adit un el « relacin de pertenencia entre un elemento v un conjunto, + predicado para decidir si un conjunto es vac, ‘© quitar un elemento a un conjunto, ‘unig do conjuntos, «@ interszceién de conjuntos. diferencia de conjuntos, y © cardinal de un conju, 26 _Estructuras de datos y métodos algortmicos Soluei Al igual que la especificacién de las cadenas en el Ejercicio 1.5, la especificacién de los conjuntos esti parumecricada con respecto al parémetro ELEM=, definido en la Secci6n 1.1.5, que exige un tipo denominado elemento sobre el cual se consiruyen los conjuntos asf como una operacién de igualdad denotada ==, que ahora nes hace falta concretamente para especificar la relacién de pertenencia y la ‘operaciOn que quita un elemento a un conjunto, La siguiente signatura declara todas las operaciones como totales, especificacién CONJUNTOS[ELEM=] usa BOOLEANOS, NATURALES tipos conjunto operaciones gito-vacio > conjunto aiecir elemento conjunto —> conjunto unit elemento > coniunto esti? elemento conjunto —> boo! es-cfowacio? : conjunto — b00! guitar ‘elemento conjunto. —> conjunto unin Conjunto conjunto. —> conuunto imterseccin : conjunto conjunto. —» coniunto iferencia conjunto conjunto. —> conjunto cardinal conjunto — nat variables €, f + elemento x. 2 eonjunto ‘Una primera posibilidad es considerar como constiuctoras cto-vacio y afiadir, pues todo conjunto finito puede obtenerse a partir del vacfo afladiendo elementos de uno en uno. En tal caso, el orden ea que se ailaden los elementos no importa, y tampoco el niimero de veces que se afiade un mismo elemento, puesto «que un conjunto se caracteriza simplemente por los elementos que pertenecen a é!. Por consiguiente, las constructoras no son libres y tenemos ecuaciones de equivalencia entre términos construidos que expresan la “conmotatividad” y la “idempotencia” de la operacién afadi. ecunciones | primera versisn } afadir(e, afadir(f.x)) = afadir( f, ahadir(e, x)) { conmutatividad } afadir(e, afadir(e, x)) = afadir(e, x) [ idempotencia } La operacicn unit se define directamente en términos de las constructoras. unii(e) = afadir(e, cito-vacto) Los predicados que comprueban si un conjunto es vacfo y si un elemento estd en un conjunto se definen de forma sencilla distinguiendo los dos casos para las constructoras es-jto-vacio?(cjo-vacio) = cierto escjto-vacio?(ahadi(e,x)) = falso esté(e, cjto-vacio) = false esti%(e, afadir(f.x)) = e¢== f v estar(e,r) La operacién quitar podfa ser considerada como parcial decidiendo que solo tiene sentido quitar un elemento cuando pertenece al conjunto dado como segundo argumento. Una alternativa es considerar aque quitar es total, de forma que al eliminar un elemento que no pertenece al conjunto, este se queda igual. Esta tltima posibilidad es la que se define en las ecuaciones que siguen. Se distinguen casos sobre Especticecion algebraica de tipos atstractos de datos 27 las constructoras de conjuntos y, coincida o no con el que se afiade. el caso no vacio, dos subcasos segiin que el clemente que se quita quitare, ftovacioy = gito-vacio quitar(e, afadr(e, x)) = quitarte, x) guitare, ahadir(f.x)) = afiadin(f. quitarle,)) <= eH f La segunda esuacién en la definicién anterior de quitar merece un par de comentarios. En primer lugar, nétese que la variable e aparece dos veces en el lado izquierdo, con lo que se hacen coineidir los dos elementos; otra posibilidad equivalente seria usar dos variables diferentes e y f como en la tervera ccuacidn, y tener una condicién & f (otambiéne = fy: quitar(e, afiadi( f..)) = quitar(e,x) = & f En ambos easos se trata de cubrir el easo contrario al cubierto por la tercera ecuaciéa, Nétese que, de acuerdo con la convencién explicada en la Seccidn 1.1.2, e == f en la condicién anterior es una abre- viatura de 1a ecuaci6n (¢ == f) = cierto que es equivaleate a Ia ecuacién e = f por los requisitos impuestes sobre el predicedo de igualdad en la Seeci6n 1.1.5, Por estas razones, es equivalente utilizar la misma variable a imponer una condicién de la forma anterior, ya que en cualquier caso se exige que dos elementos coineidan, Por otra parte, uno podria esperar, erréneamente, que el lado derecho de la segunda ecuaci6n fuera simplemente el término x; esi es incorrecto porque las ecuaciones que aparecen en la especificaci6n han de ser aplicables a textos los tgrminos. Asi, en este caso no podemos garantizar que en el término que representa al conjunto no haya repeticiones (en el sentido de haber afiadido varias veces un mismo elemento) y, por tanto, tenemos que hacer una Hamada recursiva mediante el término quilar(e. x), para aseguraros de que al final se ha quitado “por completo” el elemento ¢ en el conjunto x. Como Ia unién es conmutativ se pueden distinguir casos en el primero o en el segundo argumen to, Las ecuaciones siguientes consideran la primere posibilidad, pero la otra seria equivalente. Para la intersecei6n ocurre lo mismo, pero en esta ocasidn también hemos de distinguir subcasos segin que el elemento que se afiade est 0 no en el conjunto dado como segundo argumento; para ello usa predicado esié? en la condicién, unionicjte-vacio, yy) = y unidn(afadir(e, x), y) = afadirle, unidnc. »)) interseccién(cito-vacio, y) = _ejto-vaci interseccion(atadir(e,.x), y) = inlerseccién(x, y) <= este, y) interseccibn(atadir(e,.«), y) = afadir(e,interseccion(x, y)) <= est4?(e, y) La diferencia entre conjuntos no es conmutativa, por lo que es importante ver en qué argumento se distinguen casos. Las ectaciones siguientes lo hacen en el segundo argumento y se basan en la operaci6n aquiter definida anteriormente, teniendo en cuenta que un conjunto no Narfa al qi contiene Je un elemento que ne aiferencia(x, qto-vacio) = = x diferencia. ahadi(e. y)) = diferencia(quitar(e. x). ») Para calcular el cardinal de un conjunto distinguimos los casos correspondientes a las dos construc- toras, pero en el caso no vacfo hay que tener cuidado para no contar un mismo elemento més de una vez. Como hemos visto en las ecuaciones de la operacién madificadora quitar, el resultado tiene que ser inde- penciente del representante elegido. Al no poder garantizar que en el término que representa al conjunto no haya repeticiones, usamos quitar(e, x) en el lado derecho de la ecuacién para aseguramos de que cl elemento ¢ ya no vuelve a convarse en fa Hamada recursiva. aa You have either reached a page that is unavailable for viewing or reached your viewing limit for this book. aa You have either reached a page that is unavailable for viewing or reached your viewing limit for this book. 30_Estructuras de datos y métodos algor variables e, fs elemento x, mutticonjunto Consideramos como operaciones constructoras el multiconjunto vaefo, meto-vacio, y afiadit, puesto que todo malticonjunto finito puede ser construido a partir del vacfo, afadiendo sucesivamente elementos de uno en uno. En este caso, las consiructoras tampoco son libres, pues hay una ecuaciGn de equivalencia entre términos construidos, debido a que no importa el orden en que se aiiaden los elementos (conmu- tatividad). En cambio, ahora sf importa el nimero de veces que se afiade un mismo elemento, pues ese ‘niimero es precisamente su multiplicidad en el multiconjunto. ecuaciones afiadi(e, afadir(f,x)) = ahadi(f, aftadr(e,x)) —{ conmutatividad | Para definir el resto de las operaciones (observadoras y modificadoras) en términos de las construc- tomas conviene, en general, distinguir los dos casos y, en el caso no vac‘o, si hace falta, comparar con el ‘otro argument, que es un elemento. es-mejto-vacio?(mejto-vacfo) = cierto es-mojto-vacto?ahiadir(e, x)) = also muttip(e, mejto-vacio) 0 mulip(e, afadir(e. x1) multip(e, x) +1 muttip(e, aadir( f,x)) = multip(e,x) = ¢ 4 f Néiese que las especificaciones de las operaciones quitar y borrar son muy similares. La tinica di- ferencia aparece en la segunda ecuacién, en la cual para quitar se devuelve directamente el témino x. pues solamente hay que quitar una aparicién del elemento, mientras que para borrar se hace una llamada recursiva borrar(e, x), porque hay que quitar todas las apariciones del elemento en el malticonjunto. quilar(e, mglo-vacio) = mejto-vacto quiar(e, afadir(e,x)) = x quitar(e, afadir(f.x)) = afadin f.quiar(e,x)) = ef borrar(e, mejtoacic) = mojio-vacio borrar(e, afadir(e,.x)) = borrar(e. x) borar(e, afadi(f,x)) = afadir( f,boware,x)) = oF f Las siguientes definiciones de las operaciones unién, interseccién y diferencia para malticonjuntos siguen exactamente el mismo patron que las vistas para las operaciones con el mismo nombre subre con- juntos en la primera versién de la sclucién del Ejercicio 1.7. La tnica diferencia es que las condiciones para la interseccién se basan en la operaci6a multip, que calcula 1a multiplicidad, en vez de en el pre- dicado de pertenencia, el cual no es necesario en esta especiticacion dado que un elemento esta en un multiconjunto si y solo si su multiplicidad es mayor que cero. unién(mcjto-vacio, y) = y unién(afadi(e, x). y) = afadir(e, unién(x, y interseccién(mejto-vacio, y) = mejto-vacio intersecci6n(afadir(e, x), y) = interseccion(x, y) <= muttp(e, y) interseccién(afiadir(e..x), v) = afiadir(e, interseccion(x, quitar(e, y))) <= multip(e. y) > 0 diferencia(x, mejto-vacio) = x Giferencia(x, afadir(e. y)) = _diferencia(quitar(e. x), y) Para terminar, la diferencia entre las dos operaciones que calculan el “cardinal” de un multiconjunto aparece en el argumento de la Hamada recursiva, En la primera operacin, cardinal, como se tienen en 1.9. Es pec bstracios de datos 31 cuenta les multiplicidades, la Hamada es cardinal(x), de forma que se volverd a coniar el ele si vuelve a aparecer, mientras que en la segunda operacién, cardnal-dist, que solo cuenta los elementos distintos y no tiene en cuenta sus multiplicidades, la Hamada es cardinal-dist(borrer(e, x) que garantiza que el elemento ¢ ya no se volverd a contat cardinal(mcjtovacio) = 0 cardinal(ahadir(e,.r)) = cardinal(x) +1 cardinal-dst(mojto-vacio) 0 cardinal-det(ahadi(e, x)) = cardinal dist(borrar(e, x)) + 1 fespecificacién Especificar los polinomios con coefictentes naturates en una indeterminada, con las operaciones si wientes: polinomio nulo, sumar un monomio a ua polinomio, suma de dos polinomios, # producto, # calcular el coeticiente de cierto g predicado para decidir si un polinomio es nulo, © calcular el grado, y aluar un polinomio para un valor éado de te indeterminada, oluci Empezamos escribiendo la signatura que declara como totales todas las operaciones del enunciado, junto con una operaci6n privada mult-mono que multiplica un monomio por un polinomio, cuya utilidad se verd mas adelante, especificacién POLINOMIOS-NATURALES usa BOOLEANOS, NATURALES {pos polnomio operaciones poli-nulo — polinomio { constsuctora } sumar-meno : nat nat polinomio —> polinomio { constructora } cumer polinomio polnomio —> polinomio rmultiplicar olinomio potinomio —> —polinomio coeficiente nat polinomio — nat es-poli-nulo? : polinomio — boot grado olinomio, —> nat eval olinomio nat —>+ nat operaciones privadas mult-mono : nat nai polnomio —> polinomio variables di, jun: nat .4 = poinomia ‘Todo polinomio (sobre naturales) no nulo se puede representar de forma Gnica como una suma de la forma 32_Estructuras de datos y métodos algoritmicos donde los c; soa cosficientes naturales, yf 0, y 1 es el grado de! polinomio, Naturalmente, podemos climinar de la sama aquellos monomios cuyo coeliciente sea 0. Urilizando las operaciones de ta especifi- acidn, tal polinomio se puede representar de la forma sumar-mono(cy.0, sumar-mono(cy, 1... sumarmono(cy,1t, pol:mulo)....))- Por tamto, las operaciones peli-nulo y sumar-mono sirven como constructoras, Necesitamas ecuaciones de equivalencia entre términos construidos, puesto que sumar un monomio con coeficiente cero no modifica el polinomio, 1a summa de monomios es conmutativa, y sumar dos monomios con el mismo exponente es equivalente a sumar un dinico monomio con la suma de los coeficientes y el mismo exponente. ecuaciones sumar-mono(0, i p) P sumar-mono(c. i, sumar-mono(d, j. p)) = sumar-mono(d. j, sumar-mono(c. i. p)) sumar-mono(c, i, sumar-mono(d, i, p}) = sumar-mono(e +d, i, p) La especificacién de fa suma distingue dos casos para las constructoras en el primer argumento (de- bido a la conmutatividad de fa soma seria equivalente hacer la distincién sobre el segundo argumento),. devolviendo el segundo argumento al sumar el polinomio nulo, que es el elemento neutro de la summa, ¥ haciendo una Hamada recursiva cuando se suma un monomio, sumar(poli-nulo, 4) =4 sumar(sumar-mono(c, i, p),@) = sumar-monofe, 7.sumar(p. 4)) Anes de especificar el producto de polinomios, especiticamos la operaci6n privada que multiplica un monomio por un polinomio, distinguiendo casos en el tercer argumento. que es de tipo polinomio, Cuando el polinomio es nulo, el resultado también lo es; mientras que si es una suma de un monomio, se aplica la distributividad del producto con respecto a la suma, para hacer por un lado una Hamada recursiva con el polinomio restante q y por otro un producto de dos monomios, para lo cual se multiplican sus coeficientes y se suman sus exponentes, obteniéndose asi un monomio que se sumat al resultado de ta Hamada recursiva mutt-mono(e, i, poli-nulo) poli-nuio mutt-mono(e, i, sumar-mono(d, j.q)) = sumarmono(e +d, + j, mult-mono(e. ig) Para moltiplicar polinomios, basta distinguir casos en el primer argamento (en el segundo seria equi valente por la conmotatividad). Cuando el polinomio es nulo, el resultado también lo es; mientras que si ces una suma de un monomio, se aplica Ia distributividad del producto con respecto a la suma para hacer por un lado una Hamada recursiva con el polinomio restante ¢, y por otro, el producto de un monomio ‘por un polinomio, para lo cual se utiliza la operacién auxiliar mult-mono que acabamos de especificat, ‘mutipicar(pol-nulo, @) = pol-nulo mutiplicar(sumar-meno(e. i, p),q) = sumar(mul-monotc, iq), mutiplicar(p. q)) Para caicular el coeficiente asociado a un exponente dado, distinguimos casos sobre constructors y ‘cuando se trata de la suma de un monomio comparamos los correspondientes exponentes; el subeaso en ‘que coinciden se especifica usando una misma variable i dos veces en el lado izquierdo de la segunda 0 ¢ +coeticiente(i. p) coelicienteli, p) <= i # j coeficiente(i, pol-nulo) coeficiente(i. sumar-mono(e.i. p)) coeficiente(i, sumar-mono(c, j. p)) El predicado para comprobar si un polinomio es nulo distingue los dos casos sobre constructoras y hace una Hamada recursiva en el segundo caso, pues el polinomio nulo también puede representarse como, tuna suma de monomios nulos (con coeficiemte 0). ___Espesiticacién algebraica de tines abstractos de datos 33 1.10. ‘es-pol-nulo? (pol-nulo) cierto es-pol-nulo?(sumar-mono(c. i. p)) = 0 A es-poli-nulo?%(p) Para calcular el grado distinguimos casos sobre las constructoras del polinomio; en el segundo caso, cuando se suma un monomio nulo se descarta y se hace una Hamada recursiva, mientras que cuando el monomio no es nulo (c # 0), teniendo en cuenta que los coeficientes son mimeros naturales que no pueden anularse entre sf al sumar, el grado sera e1 maximo (operacién disponible sobre naturales, seguin la especificacidn del Ejercicio 1.2) entre el grado del monomio (que es i) y el grado del polinomio restante que se calcula recursivamente, grado(pot-nulo) = 0 grado(sumar-mono(0, i. p)) = grado(p) grado(sumar-mono(c, i, p)) = max(i,grado(p)) = ¢ #0 Para evaluar un polinomio sobre un valor n distinguimos casos sobre fas constructoras de! polinomio: cen el caso nulo el resultado es 0, mientras que en el caso de la suma de un monomio se hace una Hamada recursiva y s° suma el valor obtenido al evaluar el monomio sobre n, lo cual se consigue utilizando las ‘operaciones apropiadas sobre los niimeros naturales (exponenciacién, producto y suma). eval(polinvlo.n) 0 eval(sumar-mono(c, i, p).) = ¢ expla, i) + eval(p.1) fespecificacion Moditicar adecuadaments la especificaciGn de tos polinomios en a solucién del Ejereicto 1.9 para que los coeficientes sean mtineros eneros en vez. de naturales, Solucién: Si los coeficientes son niimeros enterns en ver de naturales, las tinicas opericiones cuya especificacién hay que cambiar son es-poli-nulo? y grado, debido a que un coeficiente positive puede ser anulado al sumar uno negativo (con el mismo exponenie). Usamos una operaciGn auxiliar quitar-exp, declarada como privada en la signatura que sigue, que “anula” el coeficiente correspondiente « un exponente dado. ‘especificacién POLINOMIOS-ENTEROS usa BOOLEANOS, ENTEROS, NATURALES tipos polinomio operaciones pol-nuto + patinonio { constructora } sumar-mono : ent nat polinomio —» polinomio | constructora } sumar polinomie polinomio —> —polinamio muttipticar Polinomio poinomo —> potinanvo coetiienta nat polinomio — ont ‘es-poli-nulo? : potinomio > boot ‘grado polinomio — nat eval polinomio ent — ont operaciones privadas mutton: ent nat poinomio —> polinomio quitar-exp nat polinomio > polinomio variables c.d.n ent i,j :nat P.@ : polinomio 34 _Estructuras de datos y métodos algoritmicos 411. ‘A continuaci6n solamente mostramos las ecuaciones que son nuevas 0 necesitan ser modificadas con respecto a la especitficaciGn POLINOMIOS-NATURALES de los polinomios con coeticientes naturales vista enel Ejercicio 1.9. Primero especificamos la operacidn auxiliar quitar-exp. distinguiendo los dos casos de las constructo- ras de polinomios y dos subcasos en el caso de la suma de un monomio, segtin coincida el exponente que se quita con el que se suma (ndtese Ia aparicién de la misma variable i dos veces en el lado izquierdo de Ja segunda ecuacién) 0 no (lo cual se comprueba con la condiciGn i # j). En ambos subeasos se hace una Mamada recursiva para asegurarse de quitar posibles apariciones repetidas del mismo exponente. ‘ecuaciones quitar-exp(/, pol-nulo) poli-nulo quitar-exp(i, sumar-mono(e, i, p)) = quitarexpli, p) quitar-exp(i, sumar-mono(e, j. p)) = sumar-mono(c, j.quitar-exp(i, p)) = i 4 j EI caso recursivo del predicado as-poli.rulo? es ahora mis complejo debida, como deciamos mis arriba, a que ciertas sumas de monomios pueden anularse entre si. Por ello, consideramos el coeficiente “global” asociado al exponente i, que se obtiene mediante el término ¢ + coeficienta(, p), y vemos si es cero; el polinomio seré nulo cuando el resto de los coeficientes sean también cero, lo cual se comprueba con una llamada recursiva, asegurandose antes de que el exponente i ya no se vuelva a tratar, usando para ello la funci6n auxitiar quitar-exp. e-poli-nulo? (poi-nulo) = cierto es-poli-nulo?(sumar-mono(c,i, p)) = ¢-+coefciente(i, p) ‘A es-poli-nulo?(quitar-exp(i, p)) La especificacién de grado sigue la misma idea que la de es-polinulo? en el caso recursivo. Se trata por tun lado el coeficiente “global” asociado al exponente i y por otro el resto del polinomio, una vez anulado ese exponente (adtese €] uso reiterado de la operaciGn auxiliar quitar-exp). El grado serdi el maximo entre 7 yeel grado de quiter-exo(i, p). que se calcula recursivamente. En vez de wilizar la operacién max como se ha hecho en el caso de polinomios sobre naturales, a continuacién se distinguen casos comparando ‘ambos valores, para que se vean diferentes posibilidades. grado (poli-nulo) =0 {grado(sumar-meno(e, i. p)) i & c+ cveteciente(’, p) #0 A grado(qutar-exp(i, p)) i fespecificacién Expecificar un tipo de datos para representar figuras como sucesiones de trazos A (arriba), B (abajo), D (derecha), I (izquierda) de longitud unitaria, con al menos las siguientes operaciones: «© gitar la figura 90 grados en sentido horario, © duplicar el tamafio de la figura, caleular la altura méxima de ta figura © calcularla anchura méxima de la figura, y «© calcularel drea del rectingulo mfnimo que contiene la figura, Solucién: Especificamos primero un tipo bisico de trazos que seri utilizado para formar las figuras, y sobre el cual definimos la operaci6n de girar un trazo 90 grados en sentido horario, diciendo directamente cul es el resultado del giro para cada uno de los cuatro trazos posibles: por ejemplo, al girar 90 grados en Sentido horario el trazo A (arriba) se obtiene el trazo O (derecha), aa You have either reached a page that is unavailable for viewing or reached your viewing limit for this book. aa You have either reached a page that is unavailable for viewing or reached your viewing limit for this book. aa You have either reached a page that is unavailable for viewing or reached your viewing limit for this book. F CAPITULO IMPLEMENTACION DE TIPOS ABSTRACTOS DE DATOS eee Habiendo dedicado el capitulo anterior a la especificacién slgebraica de tipos abstractos de datos, bordar en este capitulo el aspecto complementario, que es la implementacién de los tipos de En primer lugar, las implementaciones que vamos a realizar en este libro seguirin el paradigma de programacién impera a todos los algoritmos que se van a presentar a lo large de todo el libro. ivo; esto mismo se api De la misma forma que en las especificaciones de tipos de datos usamos un lenguaje abstracio de especificacién, descrito en la Seccién 1.1.3, para las implementaciones y algoritmos usaremos un len guaje abstracto de programacién imperativa, que describiremos en la siguiente seccién, y que puede considerarse una simplificacién de lenguajes al estilo de Pascal. En la primera fase de la implementaci6n de un tipo de datos hay que decidir cémo representar el tipo que se define en términos de tipos bésicos, como boo! (valores booleanos), nat (niimeros naturales), ent (nuimeros enteros), etc., construcciones bésicas como vectores y registros, y otros tipos implementados anteriormente. De esta forma se obtiene una representacién intema concreta, que se conoce en la imple- ‘mentacién pero que se oculta (por la propia metodologia, independientemente de mecanismos concretos que un lenguaje pueda proporcionar ademiés para ello) a efectos de usar el tipo de datos, que solamente se puede hacer mediante las operaciones de la interfaz del tipo de datos en cuestisn, En la segunds fase hay que escribir el «lgoritmo correspondiente a cada operacién de Ia interfaz de! tipo de datos y tal vez alguna operaci6n auxiliar adicional, usando la representacién concreta definida en la primera fase. ‘Vamos a clasificar las posibles representaciones en dos grandes clases: « estéticas, definidas tipicamente en téminos de un vector (de una 0 més dimensiones), de forma que el tamaio del vector esta determinado por cierta constante Nes decir, la cantidad de memoria asignada a la estructura no cambia a lo largo de su vida, Como la constante NV esté determinada @ priori, puede ocurrir que sea demasiado grande y se desperdicie memoria, o que la cantidad de memoria asignada se agote y esto dé lugar a errores por falta de espacio. «© dindmicas, definidas tipicamente en términos de memoria dindmica © punteros (véanse la Sec- cidn 2.1.2 may adelante y el Ejercicio 2.8 al final del ema), de forma que la memoria se va asignando segtin se necesita, con lo cual se solucionan los problemas que se planteaban en una implementacién estitica, aunque tal vez a cambio de una mayor complejidad en la programaci6n. 40__Estructuras de datos y métodos algoritmicos Una parte esencial de la programacién de cualquier algoritmo, incluyendo los que implementan las operaciones de un tipo de datos, es el estudio de su coste en tiempo y en memoria, En general, cuando se habla de coste sin mis nos referimos a coste en tiempo en el caso peor, Cuando se trate del coste en tiempo en el caso mejor, de coste en tiempo en el caso medio 0 de coste en espacio, 1o haremos explicito. En os algoritmos que siguen, cuando el calcuo del coste es inmediato, lo escribimos al lado det algo- ritmo, sin ninguna explicacién adicional al respecto, Urtilizamos la notacién asintstica habitual, descrita por ejemplo en {PeA98, Capitulo 1] y en casi todos los textos sobre algoritmos. Un detalle importante es que en este libro no vamos a considerar la verificacién de las implementa- ciones con respecto. laespeciticacién de un tipo de datos. Esto no significa en absoluto que este tema no merezea atencién, sino que se trata de un tema importante al cual se le dedica todavia mucho trabajo de investigacién, pero que nos parece que requiere demasiado formalismo a un nivel de detalle que no nos ha parecido conveniente incluir en el libro. Por tanto, confiarmos en que las intuiciones que acompaitan cada especificacion y su correspondiente implementaci6n sean suficientes para relacionarlas, 2.1.1, Lenguaje abstracto de programacién imperativa A continuacidn describimos la imperativa a lo largo del libro. instrucciones que usaremos en el lenguaje abstracto de programacién © nada es Ia insiruccién que no hace nada, como su nombre indica, © axignacion: La instruceiGn rink asigna el valor de Ja expresion £ a la variable 2x, que tiene que ser del mismo tipo que E. La asignacién puede ser milriple, de la forma Crna cette (By Bass End para n > 1, cuya ejecucisn asigna el valor de cada £ simultincamente a cada variable 3y, para i entre | y n. Cada variable debe tener el mismo tipo que el de la expresi6n correspondiente cuyo valor se le va a asignar, y todas las variables de la parte izquierda de la asignacidn miltiple tienen que ser diferentes. # secuencia: Dados dos programas Py P:, en una instruccién secuencial Ps Normalmente no escribimos DB, > Pa feasos sirve para distinguir casos de acuerdo con el valor de las expresiones booleanas Bi, .... By Conoci das como guardas. Se evalian todas las guardas y a continuacidn se ejecuta el programa F; tal que Bj esciera, Suponemos que todas las guardas son disjuntas y que cubren todas las posibilidades, Implementacion de tipos abstractos de datos 41 © condicional: La instuccion si B entonces P; sino Py fi es equivalente ala siguiente distincidn de casos: cases Bo A IB A feasos Cuando P; ¢s la instruceién nada, simplemente escribimos si B entonces P; fi bucle: La instruccién mientras B hacer P fmientras evalia la condicién booleana B y si es falsa se termina la ejecucisn del bucle, mientras que si es ciemta se ejecuta P y, a continuacién, se vuelve a evaluar B y se repite el proceso, Esta instruc ci6n introduce 1a posibilidad de no terminaciGn en los programas, por lo que hay que asegurar su terminacién mediante alguna funcidn de cota cayo valor se decrementa en cada iteracién del bacle. © bucte con contador: La insteucciéa para i = inicial hasta final paso p hacer P fpara donde inicial, final y p son expresiones del mismo tipo numérico (de hecho, p suele ser una cons- ante), con p 3 0, és equivalente a un programa con un bucle. Si p > 0, el programa equivalente iniciat mientras i < final hacer P tositp fmientras inicial mientras i > final hacer Ps iisitp mientras, En ambos casos, i es una variable del mismo tipo numérico que no se modifica en P, Nétese que cuando el rango del contador entre inicial y final es vacio, no se realiza ninguna iteracién del bucle. Cuando p = 1, que es el caso mas habitual, omitimos la menciéa del paso en el bucle, «© entrada: la instruccién leer(c) lee un carécter del dispositivo de entrada apropiado (teclado, fichero, etc). «# salida: ia instruceién imprimir(mersaje) imprime un mensaje (cadena de caracteres) en el dispositi- vo de salida apropiado (pantalla, fichero, impresora, ec.) © error: ta instrucci6n error(nensaje) aborta la ejecucién del programa e imp error, Ye un mensaje de © comentarios: se escriben entre Haves en el lugar del programa que convenga, 42__Estructuras de datos y métodos algoritmicos El lenguaje abstracto esti tipado, por lo que las expresiones tienen que tener el tipo apropiado para set correctas, como ya se ha mencionado al hablar de la instruccién de asignacién. En particular, todas las variables tienen un tipo asociado, como veremos a continuacién. ‘Tenemos los siguientes tipos y construcciones de tipas basicos: + b001, con valores booleanos cierto y fa'so, y las operaciones booleanas habituales de negacién ~, conjuncién A y disyuncién V. En algunas ocasiones es importante utilizar las versiones de estas dos titimas operaciones que se evaliian con cortocircuito: Ac, que cuando el valor de su primer ‘argumento es falso ya devuelve directamente falso, sin evaluar su segundo argumento, y Vc. que cuando el valor de su primer argumento es cierto ya devuelve directamente cierto, sin evaluar su segundo argumenio, ‘© tipos numéricos nat (naturales), ent (enteros) y real (reales), con Jas operaciones aritméticas y re- lacionales habituales. Cuandos los mimeros naturales 0 reales sean positivos, les asignaremos tipos rat’ y real. Algunas veces conviene considerar dominios numéricos extendids con valores infi nitos; en tales ocasiones utilizamos 10s tipos Nac, ent: y rear. acteres car. © tipo de © tipos enumerados (valor)... valor), con k= 1 ‘» rangos i..j donde y j son mimeros naturales. Si f j, el rango es vacio. ‘ vectores: Una variable V de tipo vector se declara V{i..j] de tipo con su rango ademis del tipo de sus elementos. A una posicidn k en el rango del vector, es decir, con i < k = j, se accede con la notaci6n V[k}, tanto para leer la informacién come para modificarla. Una asignaciéa de la forma Vi..i} == [Valor] modifica el contenido de todas las posiciones en el rango entre i y j. Los vectores pueden ser de mas de una dimensién. « regisiros: Un tipo registro reg campo, : tio, .... campo, : tivo, freg se declara con sus campos y tos tipos correspondientes. Si R es una variable de eve tipo, R.cumpo, accede a la informaciéa del ‘campo con nombre campo,, tanto para leerla como para modificarla, © punteros, deseritos en la Seccién 2.1.2. Por otra parte, como veremos a lo largo de este capitulo (y también en el resto de la primera parte del libro), en el lenguaje se definen los tipos de datos que se deseen a partir de los tipos basicos y de otros definidos anteriormente. A la hora de escribir los algoritmos, distinguimos entre funciones y procedimientos. Una funcién se declara con varios pardmetros de entrada (tal vez ningune como caso extremo cuando la funcién es constante) y al menos un pardimetro de safida (o resultado), cada uno de ellos con su tipo correspondiente. fun nombie-funter : p01, ---+€n £ Ho,) devs: tipo! var x): tp0....Xx tipo! P tun ‘Cuando Ia funcién tiene mas de un pardmetro de salida, estos se dk Sim! Poly). Con m > 1 wran de la forma (s1 = tipo, Los parimmeteos de entrada no pueden cambiar a lo largo de la ejecucién del cuerpo P de la func por lo que. si el algoritmo modifica esos datos de alguna forma, es necesario realizar una copia (véase la Seecidn 2.1.3). En particular, no se permiten asignaciones a pardmetros de entrada, Ademis, en el cuerpo P se pueden usar variables auxiliares locales declaradas tras la cabecera. En general, no hacemos explicitas las declaraciones de variables auxiliares de tipos basicos, tales como boo! © nat (como, por ejemplo, las variables usadas como contadares en bucles con contador). pero hay que tener en cuenta que todas las variables que no son pardmetros de entrada o Salida Son variables auxitiares locales, y nunca hay variables globales __Implementaciin de tipos abstractos de datos 43 ficar a lo largo de la ejecucién, v pardmetros de entrada que no cambian y que se declaran precediéndolos Por otra parte, un procedimiento puede tener parimerros de entrada/salida, cuyo valor se puede modi- conel simbolo e. | proc nombre-procte e1 var x1 : tipd, - fproe tipo}... en: 100,081: te... 80: UDO) Aunque en este esquema aparezcan primero los parimetros de entrada y luego Ios de entrada/satida, cen general las diferentes clases de parimetros se pueden intercakar Cuando en Ia segunda fase de ta implementacién de un tipo de datos vamos a programar las ope- raciones correspondientes, en funcidn de la representacién concreta interna definida en Ia primera fase, hay que decidir para cads operacién si se implementa como funcion o como procedimiento. En algunos 0s, como por ejemplo para las operaciones observadoras, Ia eleccidn esti clara, dado que estas ope- raciones consultan una estructura de datos sin modificarla, y por tanto se comportan claramente como funciones. Sin embargo, en otros casos la situaciGn puede no ser tan clara, En general, nuestro enfoque es implementar como procedimientos aquellos algoritmos en los cuales una estructura se modifica e intuit Yamente uno espera que el resultado sea la misma estructura modificada y no una copia modificada de la original. Por ejemplo, muchas estructuras (pilas, colas, lista, conjuntos, etc.) se comportan como “con- tenedores” con elementos que se ahaden 0 se quitan, y en tales casos los “contenedores” no se copian. En Imbio, cuando se hace la unin de dos conjunios. ln idea iniuitiva es que se tiene un nuevo “contenedor” ‘con los elementos de las dos estructuras de partida y por tanto se implementa como una funcidn En algunas ocasionesusamos la notacién de precondicién y posteondicidn para dar informacién sobre Ja entrada y la saltda de un algoritmo, Especificamente, la precondiciGn es una expresion booleana que expresa las condiciones sobre los pardmetros de entrada de un algoritmo que del algoritmo tiene sentido, ademiis de los tipos: por ejemplo, un algoritme de divisiGn sobre un tipo numérico solo tiene sentido cuando el divisor no es nulo. La posteondieién es una expresién booleana que relaciona los parimeiros de salida eon los de entrada, indicando de esta forma qué eéleulo.o proceso realiza el algoritmo sobre los datos de entrada, Bs eee ees Como ya hemos mencionado al principio de esta introduceién, vamos a construir implementaciones de estructuras de datos con memoria dindmica. Laidea consiste en utilizar tipos definidos mediante registros que incluyan algsin campo cuyo valor sea un puntero que sefale a un registro del mismo tipo, y que seri utilizado para “encadenar” pesiciones de memoria, Un ejemplo tipico de tipos para representar estructuras lineales enlazadas es el siguiente: tipos ‘enlace = puntero a nodo nodo = reg valor : elemento sig : enlece freg ftipos Un valor de tipo nodoes un registro con dos campos, en uno de los cuales se guarda cierta informacion de tipo efementoy en el otro hay un “enlace”, que es un punteroa otro valor del mismo tipo nado. [Notese que esta definiciGn de tipos es mutuamente recursiva puesto que el segundo campo de un nodo que, a su vez, ex un puntero aun nodo, El caso basico se obtiene cuando el puntero es nulo, es decir, tiene el valor constante nil que indica que el puntero no apunta a ninguna posicién de memoria. es un enla aa You have either reached a page that is unavailable for viewing or reached your viewing limit for this book. aa You have either reached a page that is unavailable for viewing or reached your viewing limit for this book. 46 _Estructuras de datos y métodos algoritmicos fun urit(e : elemenio) dev x: conjunto { O(1)} xaittima = 1 x.contenido{ 1] fun ¢ fun es-jtovacio?(x : conjunto) dev & : boot { @(1) } ara las tres representaciones In operacidn est? se puede implementar con un eoste en © x.tiltimo) (es decir, lineal con respecto al tamafio de fa parte ocupadsa del vector), haciendo un recorrido del vector desde el principio hasta que o bien se encuentra el elemento buscado, o bien se llega al final de la parte ‘ocupada. El algoritmo queds como sigue: fun esta? (c : elemento, x : conjunto) dev b: bool { OCw.tltime) | b = felso I emtras i < xuiltino \ —b hacer (contenido fimist fmientras ‘fun Sin embargo, para la Ghtima representacién, aprovechando que los elementos estin ordenados, se puede obtener una implementacién mucho mis eficiente, con un coste en © (log(a.tiltino)), utilizando buisqueda binaria (véase el Ejercicio 11.1), para lo cual (VII... xuiltimo entonces n= n +1 fpara ‘fun 50__Estructuras de datos y métodos algoritmicos 22. Una tercera posibilidad para la primera representaciGn es utilizar un conjunto auxiliary en el que solamente guantamos los elementos diferentes. Al final del procedimiento, como y es una versién opti- mizada de s en la que se han eliminado posibles repeticiones, el contenido de y se copia en x mediante tuna asignacién entre registros, para mejorar asf futuros accesos a x y reducir al mismo tiempo el espacio cupado en el vector para su almacenamiento. Nétese que cardinal-a2 debe implementarse como proce- dimiento en vez de como funcién porque quita repeticiones al mismo tiempo que se calcula el cardinal. Sin embargo, el coste en tiempo permanece cuadritico en el caso peor. proc cardinal-a2(x : conjunto, n:nat) — { G(x.siltimo*) } vary: conjunto y = oto-vacioi) para i= 1 hasta s.tiltimo hacer si “estd%(x.contenidoli), y) entonces afadira(x.contenidoli|, fpara n= yailtino . Implementar el TAD de fos conjunios finitos (Bjercicio |. 7) sabiendo que los elementos soa aumeros naturales en ei tatervalo 1A. muy exigentes sobre los elementos, podemos simplificar considerablemente la representacién general de los conjuntos del Ejercicio 2.1, y utilizar ahora como representacién de un conjunto x un vector x[1...V] de booteanas, de forma que Vi: 1 0 entonces x[e] := xfe]—1 fal fproe proc borar(e e : elemento. : multiconunto) | @11) | xfe] = 0 fproc Las operaciones de unién, interseccién y diferencia tienen una traducei6n inmediata en operaciones sviumeticas. # Un clementoestien Ia unin de dos multiconjuntos cantas veces come en ambos, por lo que la unién. corresponde a la suma. © Un elemento esti en la intersecei ‘menos, por lo que la interseccidn corresponde al minim. de dos multiconjuntos el némew de veces que esté en el que © El nimero de veces que un elemento estéen ta diferencia de dos multiconjuntos se obtiene restando al mimero de veces que esti en el primer argumento el niimero de veces que esti en ef segundo; cuando est mas veces en el segundo, el elemento no aperece en la diferencia, fun unién(x, y : multconjunto) dev z : muticoniumto | @(N) } para i= hasta V hacer 1:5 all] +10) fara fun fun interseccién(.x, y : muiticonjunto) dev < + mutticonjunto { @(N)} para i =I hasta V hacer mincxli. yi) fpara fun fun diterencia(x, » : mutticonjunto) dev =: multconjunto | O(N) } para i =I hasta N hacer si x[i] > pli] entonees 2[i) := x[/] — ylé) sino 2[i] = 0 tsi fara ffun El test explicito que aparece en el algoritmo anterior se puede elimirar si la implementacién de la resta de naturales ya tiene en cuenta que al restar a un niimero otro mayor el resultado es cero, Para calcular el “cardinal” de un multiconjunt tos, se recorre el vector sumando todas las multip! teniendo en cuenta las multiplicidades de los elemen- idades que se van encontrando. fun cardinal(x : multiconjunto) dev n:nat { O(N) } ni=0 para n I hasta NV hacer n+xti) fpara ffun 54 Estructuras de datos y métedos algoritricos 2.4. Para contar solamente los elementos distintos, tambign se recorre todo el vector, contando ahora el rnimero de posiciones cuyo valor es un mimero mayor que cero. fun cardinaldat(x : mutticonjunto) dev n:nat (O(N) } ni=0 para i= 1 hasta N hacer st x{i] > 0 emonces n:= 0 +1 fst fpara ffun Los malticonjuntos sobre elementos que admiten una relacién de orden total se pueden implementar mediante tablas ordenadas, como veremos en el Ejercicio 7.9. Impiemtentar el TAD de los pohinomies con coeficientes naturales (Ejercicio 1.9), representando un polinomin SOF gej.x' de grado x mediante un vector P{0..N} con N > g,de forma gue Pli} = ¢; parsO <2 Sg Solucién: De acuerdo con 1a idea del enunciado, el tipo representante consiste en. un vector para guardar los coeticiemtes junto con informacién sobre el grado del polinomio, que coincide con el indice de la ultima posicisn “interesante”. tipos polinomio = reg coefs(0..V] de nat grado :0..N freg ftipos Nétese que el rango del vector empieza en 0 en vez de en | como en otros ejercicios, y que los polinomios de grado 0 son los polinomios constantes, incluyendo el polinomio aulo. En particular, el ccoeficiente guardado en la posicién del grado es distinto de 0, excepto para el polinomio nulo que tiene grado 0 y el niimero en esa posicién es tambien 0. La implementacién de las operaciones poli-nulo y es:poli-nulo? es inmediata. fun poli-nuio() dev p: poinomio { O(1)} pgrado := 0; p.coefsl0] = 0 ffun fun es-poi-nuio?(p : polinomio) dev b: boo! { (1) } (p.grado = 0) 4 (p.coefi{0] = 0) ffun Para sumar un monomio debemos distinguir casos para ver si el exponente correspondiente hace aumentar el grado 0 no. Esto puede causar un error si el tamafio del vector es demasiado pequetio. En el caso mejor el coste es constante, pero en el caso peor el costs es lineal eon respecto al valor del exponente que se afiade, debido a Ia necesidad de recorrer parte del vector y rellenarlo con ceros. Proc sumar-mono(e ¢,i: nat, p : polinemio) ( @li) ) sic £0 entonces casos i= p.grado > p.coefsti) p.cogpsli] +¢ —{ moditicar ceficiente existene } Implementacién de tipos abstractos de O pgmdo p.coef(p.srado + |i — U1 == 10] { nuevos coeficientes mulos | pecoefili} = ¢ | nuevo coeficiente no nulo } pegrado := i inerementar grado } Dis N + arronkxponente denasiado grande) feasos fi fproe ‘Como el polivomio resultado de sumar dos polinomios con coeficiemtes naturales tiene el grado igual al maximo de Tos grados de los dos argumentos, para sumar basta recorrer los dos vectores ¢ ir sumando coeficiente a coeficiente; cuando se Mega al grado minimo, se copian los restantes coeficientes del poli- nomio de mayor grado, Por tanto, el coste de Ia suma es lineal con respecto al grado miiximo. Notese que implementamos sumar sin utilizar sumar-mono. fun sumar(p.< : polinomio) dev r : polinamio | @(maxtp.crudo..q.grado}) } m 2= min(p.grado,q grado) rgrado ‘= max(p.grado, q.grido) para i =0 hasta m hacer reoepli] = p.coefsli] + ¢.coefli] fpara {copiar re para i=m-+1 hasta p.grado hacer reoepilil == p.coefili] fpara (copiar restantes de q, silos hay ) para i= +1 hasta q.grado hacer reoepsli) == 4.coefsli] fpara ffun En general, el producto de dos polinomios tiene el grado igual a la sums de los grados de los dos argumentos, pero cuando uno de estos es e polinomio nulo, el resultado es tambign el polinomio nuto; Por eso, necesitamos tratar esa sitvacién aparte. Para cl caso general, vamos @ ir sumando directamente (es decir, sin usar sumar-mono) en el vector resultado todos los monomios resultantes de todas las mul- tiplicaciones de monomios entre los dos polinomios dados. De nuevo, hay que tener cuidado con que el polinomio resultante quepa en el vector. fun muttplicar(p.q : polinomio) dev r : polinomio | O(p.gradog.grado) | es-poll-nulo?(p) v es-poli-nulo?(g) entonces r := pali-nulo() sino rgrudo = pgrato +q.grado si r.grado > N entonces error(Exponente demasiado grande) sino rcoefit0..r.gruddo] = (0 para i =0 hasta p.grado hacer para j =0 hasta q.grido hacer recoefili + J+ p.coefili| * q.coefsl 7} 25. Con esta representacisn los algoritmos para calcula un ceeficiente y el grado de un polinomio dado son muy sencillos. fun cosficienteli : nat, p : polinemio) dev ¢: nat { @(1) } i < p.grado entonces c := p.coefsli] no c= 0 tsi fun fun grado(p : polinomio) dev ¢ nat ( @(1)1 grado tun Finalmente, vamos a ver dos formas diferentes de implementa la evaluacién de un polinomio sobre tun niimero natural, En la primera utilizamos una variable auxiliar por que va calculando las sucesivas potencias vf del valor v dado, para k deede Ohacia el grado del polinomio. fun evaluart(p : potinomio, » : rat) dev w: nat { ©(p.grado) } w = p.coefildl pot = para i= I hasta p.grado hacer pot = pote w i= w+ p.coefsli] * por fpara ffun Como se hace un recorrido del vector de izquierda a derecha, el coste es lineal con respecto al grado {del polinomio, y el niimero de multiplicaciones que se hace es 2p.grudo, En la segunda forma de evaluat, ap dad: femos el métode de Homer, que se basa en la siguiente igual- cpt tegaetl te ten? ten tea = (Coe + ep Dd Ee bene ena tera beg fun evaluar2(p : potinomio, v : rat) dev w nat { O(p.grado) | p.coefst p.grado para i= p.grado — 1 hasta 0 paso —1 hacer w = wut p.coefili) fpara ffun Ahora se hace un recorrido del vector de derecha a izquierda, por lo que el coste contintia siendo lineal con respecto al grado del polinomio. pero el mimero de multiplicaciones se ha reducido a la mitad, siendo ahora igual a p.grado. Modificar adecuadamente la implementacion def TAD de tos polinomies en Ia sotuetdn del Ejent cio 24 para que tos coeficientes sean niimeros euferus en ver de naturales (tl vomio se especitics en icio 1.10), Solucién: El tipo representante consiste en un vector para guardar los coeficientes que ahora son enteros, junto con informacién sobre el grado del polinomio. __Implemeniacién de tipos abstractos de datos 57 tipos polinomio = reg coefal0..N] de ent grado :0..N freg. ftipos La diferencia importante con respecto a la implementacién de polinomios con coeficientes naturales, realizada en el Ejercicio 2.4 es que, en el caso de coeficientes enieros, no tenemos informacion a priori sobre el grado de una suma de polinomios, puesto que dos coeticientes con signo distinto se pueden anular al sumar y hacer disminuirel grado, Esto afecta a los algoritmos que implementan sumar-mono y sumar. ‘Vamos a reescribir los mismos algoritmos que en el Ejercicio 2.4, distinguiendo el caso adicional en l cual se pueden anular los coeficientes que determinan el grado. En este caso, recorremos el vector de derecha & izquierda, hasta encontrar el Gltimo eoeficiente no nulo, o ver que el grado es cero, Los costes 1 cambian, proc sumarmono(e ¢: ent, ei: nat, p: poinomio) { i) } si c £0 entonces casos i { modificar coeficiente existente, grado no cambia | pcoefli] -= p.coefsli] + ¢ Di =p.grado + { modificar coeficiente existente y recalcular grado } pcocfili] = p.coefsli] + ¢ mientras p.grado > 0.0 p.coefslp.grado| = 0 hacer p.grado := p.grado~ 1 {mientras O p.grado p-coefilp.srado + |..i ~ 1] == [0] { nuevos coeficientes nulos | p-coefilil = ¢ — { nuevo coeticiente no nulo } p.grado:= i (incrementar grado } Di>N — eronzxponente denasiado grai feasos fproc fun sumar(p. q : potinomio) dev r : potinomio | O(mixtp.¢grado, q.grudo)) } ‘m := min(p.grado, q.grado) grado := max(p.grado, y.grado) para i =0 hasta m hacer reoefili] = p.coefsl] + q.coefli] ‘para casas p.grado > m > | copiarrestantes de p | reoefsim ~ 1..p.grado] = p.coefslm + l.-p.grado] D g.grado~ m > { copiar estantes de q } rcoefsim + 1..g.gntdo) := q.coefalm + 1..g.grado] 0 p.grado = q.grado —» | recalcular grado } mientras s.grado > 0 9 rcoefslr.grado] rgrado = r.grado— 1 fmientras feasos ffun 0 hacer En la operaci para mut ar no hay que hack in cambio, puesto que el grado del polinomie producto de dos polinomios no nulos continta siendo la sumade los gradasde los dos argumentos, yaque aa You have either reached a page that is unavailable for viewing or reached your viewing limit for this book. aa You have either reached a page that is unavailable for viewing or reached your viewing limit for this book. 60 _Esiructuras de datos y métodos algoritmicos FI coste de Ia copis es lineal con respecto a puiltimo. A continuacién, en el caso peor, todos los elementos que se afiaden mediante sumar-mono se insertan a la izquierda de los elementos de p que necesitan ser desplazados, lo que supone un coste en tiempo en O(p.iltino q.tittino). Sin embargo, si 1 tamaito de p fuera mucho menor que el de q, el coste de la biisqueda podria ser peor que el coste del desplazamiento. Como la suma es conmutativa y por tanto no importa el orden de los argumentos, vamos a suponer que el tamaiio de p es mayor que el de q para concluir que el coste total esté en (patton q ttt). Para la operacin modificadora muttipicar, podrfamos utilizar una operaciGn auxiliar multmono, como hicimos en la especificacién (véase la solucin del Ejercicio 1.9), pero en lugar de eso vamos a sumar al resultado todos los monomios resultantes de realizar les multiplicaciones entre monomios de los dos polinomios dados, pero ahora invocando a la operacién sumar-mono, a diferencia de la implementacion realizada en el Ejercicio 2.4. fun muttipicar(y, q : polinomio) dev r : potinomio 1 = polisnulo() para i= hasta puiltimo hacer para j= 1 hasta quittime hacer sumar-mono(p.monomios[i .coeficiente = q.monomios|jl.coeficiente, p.monomiosli Lexponente + q.monomios! j Lexponente. r) fara fpara ‘ffun El coste esta en ©((p.uitimo g.siltimo)*), debido a los dos bueles anidados y en base al tamafio del veetor en el que se sums, Para calcular el coeficiente asociado 2 un exponente basta hacer tna biisqueda del exponente: sino se encuentra, entonces el coeficiente es nulo, El coste es logaritmico por la bisqueda. fun coaficiente(i : nat. p + polinamio) dev c nat @tlogtp.zihimo)) } (b.n) = buscarerp(p.monomios. i, L, paitimo) » entonces ¢ := p.nonomios|n| coejiciente 0 fsi ‘fun Para calcular el grado de un poliaomio no nulo basta con mirar el exponente mayor en el vector que representa el polinomio, que se encuentra en la posicién indicada por ultimo. fun grado(p : potinomio) dev g: nat { @(1)} si puilimo # 0 entonees ¢ = p.monomios|p.tiltimo|.exponente sino g:=0 fi ‘fun En la siguiente funcién para evaluar un polinomio sobre un ntimero natural, utilizamos la funcién exp. «que implementa la operacién de exponenciacién sobre naturales. El cose de algoritmo depende del coste el algoritmo de exponenciacion (que podria ser lineal o togaritmico con respecio al vater del exponente). fun evaluart(p : potinomio, v : nat) dev w : nat w para i= hasta puilrimo hacer w = w+ pamonomiosti .coeficiente * exp(v, p.nonomios{i |exponemte) fpara ffun Implementacion de tipos abstractos de datos 61 Una impiementacién alternativa, que no invoca ningin algoritmo de exponenciacién, consiste en uti: lizar variables auxiliares j y por para llevar caiculada en por ta potencia v’ del valor dado. El coste en este caso es lineal con respecto at grado del polinomio. fun evaluar2(p : polinomio, v : nat) dev w : nat { @igradocp)) | wis 0; j= 0: potr=t para {= 1 hasta p.uitimo hacer mientras j p.monomios{i|.exponente hacer { exponentes ausentes | j= jt: pots porev mientras w i= w+ pamonomiosli| coeficiente + por fpara fun Aunque este segundo algoritmo Hleva precatculadas las potencias de v para no tener que recalcularlas partiendo de cero, no siempre es més eficiente, pues puede ocurtir que el polinomio sea poco “denso ton el sentido de que tenga pocos cveficientes no nulos y cuyos exponentes scan clevados. En tales casos extremos es mejor hacer unas pocas lamadas a un algoritimo logaritmico de exponenciacidn (véase el Ejercicio 11.19) que calcular todas las potencias, Una tercera posibitidad, que nos limitamos a mencionar, es utilizar el métudo de Horner para imple- ‘mentar el procedimiento de evaluacién (véase la solucién del Ejercicio 24), 2.7. | Modilicar adeciadaneme la Unpien wacidn del TAD de lox polinomios en Ja solucién del Ejuc ‘ip 2.6 para que los coeticientey sean nui 1 Bjercicio 1.10), 1 entre en 8ez de jurales (tal comose expecili en Solucién: Aparte de cambiar los tipos adecuadamente, la ‘inica operacisn cuya implementacién hay que cambiar es sumar-mono, que ahora utiliza otro procedimiento auxiliar desplazar-iag (véase la solucion del Ejerci- cio 2.1) para efectuar desplazamieatos a la izquierda en el vector de monomios cuando un coeticiente se anula, pues en el vector solamente se guardan los monomios no mulos. proc sumarmono(e ¢ : ent,e1 : nat. p: potinomio) | puittimo | sic £ 0 entonces (bn) += busca-exp(p.monomios, i, 1. pailtimo) si b entonces p.monomios|n|.coeficiente := panononios|n|.coeficiente + ¢ si p.monomios(n}.coefciente = 0 entonces desplazar-ieq(p.monomios,n + 1. palltino) pailtino := patttino ~ 1 Bi sino si pauiltimo = N entonces error(zspacio insuficiente) desplazar-der(p.monomios,n, putltino) pmonomios|n|.cocficiente := ¢; panonomiosin).exponente := i pailtimo := pailtine +1 bi ti fi fproc 62_Estructuras de datos y métodos algoritmicos 28. il me: le e lee} es fei Figura 2.2: Estructura enlazada lineal. Las demas operaciones, o bien estén implementadas en términos de sumar-mono, como sumar y mul- tipicar, con lo cual ya se gestiona indirectamente la anulacién de coeficientes, o bien son operaciones que no dependen del tipo de los coeficientes y, por tanto, funcionan exactamente de la misma forma. Considcremos una estuctura dindiica enlazads lineal come la de 1a Figusa 2.2. Eseribiy algosiunos genéricos pars copiar y anelar una estructara enlazada lineal de dicha forma. sin suponer eonccide ef ipo elemento de fas datos que se guardan en cada nod, Soluci EL tipo que representa Ia estructura enlazada lineal se define de la siguiente manora: tipos enlace = puntero.a node noco reg valor : elemento sig : entace freg estructura = enlace ftipos ‘Como se ha comentado en la Secci6n 2.1.2, esta definici6n de tipos es mutuamente recussiva. La idea del algoritmo para copiar es que se construya en q una copia idéntica de la estructura p dada como argumento. Obsérvese que una asignacién q := p no tendria este efecto, ya que solo se haria tuna copia de punteros, es decir, los punteros p y q apuntarfan a la misma estructura, pero no habria una duplicacién de la memoria ocupada, de forma que q apunte a una estructura idéntica a Ts que es apuntada ‘por p pero en posiciones de memoria diferentes. Primero vemos una versidn recursiva de copiar. fun copiar(p : estructura) dev q : estructura var r: estuctura sip =nil entonces q := nil sino r= copiar(p sig) { copiar recursivamente el resto de la estructura } reservar(q) qt valor := copiarelem(p t.valor) { copiar elementos ) qt sig =r fi ‘fun Destaquemos la invocacién que se hace de la funcién copiar-etem para copiar los valores guardados cen los nodos. Dependiendo del tipo de tales valores, puede que la copia se haga con una simple asigna- cién, con una Hamada recursiva al mismo algoritmo copiar (cuando el tipo elemento coincide con el tipo estructura, en el caso de estructuras anidadas de la misma clase) 0 con un algoritmo de copia semejante para otra clase de estructuras (en el caso de estructuras anidadas de diferentes clases). Implementacién de tipos abstracios ce datos 63 4 fal sig. Figura 2.3: Copia iterativa de una estructura enlazada. Una versién iterativa del algoritmo copiar es como sigue. El puntero r recorre la estructura p. En el caso no nulo, después de crear el primer nodo al que se apunta con q, se van creando los nodos siguientes, apuntados port, y se van enlazando con el anterior, al que se apunta con s. Cuando se llega al final de p. es devir, cuando r = nit, se termina también la estructura copiada con otro puntero nulo. fun copiarit(p : estructura) dev q : estructura yar 13,0: enlace sip =nil entonces q := nil sino rap reservar) qt valor := copier-elem(rt valor) { copiar elementos | si=q mientras rt sig nil hacer ris rt sig reservar(t) Tf evalar := copiar-elem(r stasig ist s fimientras st sig fi fun valor) { copiar elem: El procedimiento anular recorre las posiciones de fa estructura Tiberando uno a uno cada nodo. Por si se diera el caso de que los elementos que se guardan en cada nodo necesitaran ser anulados de forma semejante, se invoea al procedimiento anular-elem que libera la memoria correspondiente. Este proce- ddimiento puede ser el mismo anular, otro semejante para otra clase de estructuras, o simplemente vacio (es decir, un procedimiento que no hace nada) en e! caso de datos de tipas bisicos como nat y boot, o consrucciones bésieas de tipos como veetores y registros. proc anular(p : estructura) var 9 : enlaco mientras p nil hacer q= Pi p= pr sig anuiarelem(q * valor) ( anular elementos | liberar(g) fmientras fproc

You might also like