Descargar la presentación
La descarga está en progreso. Por favor, espere
1
Subrutinas y Control de Abstracción
2
Agenda Manejo de Excepciones Definición
Maneras Tradicionales para manejar excepciones Manejo de Errores Propagación de excepciones Ejemplo del manejo de excepciones Usos de los mecanismos de manejadores de excepciones. Ventajas en el manejo de excepciones Definición de Excepciones Implementación de Excepciones Solución Alternativa Propiedades Iteradores Corrutinas Iteradores como Corrutinas
3
Manejo de Excepciones Excepciones Ejemplos
Condición inusual detectada a tiempo de ejecución. Tal vez requiere un “Back-out” de muchos niveles de llamadas a subrutinas Ejemplos Desbordamiento (overflow) aritmético. Fin de archivo sobre una entrada. Tipo incorrecto para la entrada de datos. Definiciones de usuario en condiciones (no necesariamente erróneas) invocadas explícitamente. Una definición formal de excepción es: condición “anormal” o “excepcional” (error o casi-error) que ocurre, durante la ejecución de un programa, de la cual el programador puede tener la opción de recuperarse, las mismas pueden ser predefinidas o definidas por el usuario. En Java las excepciones son objetos y permite definir excepciones. Un lenguaje que no provea excepciones, es considerado como un error. Cada vez los lenguajes más recientes incluyendo Clu, Ada, Modula-3, C++, Java y ML proveen manejadores de excepciones en la cual los manejadores son léxicamente delimitada por bloques de código, en la cual la ejecución del manejador reemplaza la porción del bloque en caso de ser necesario.
4
Manejo de Excepciones Maneras tradicionales para manejar dichas situaciones: Retorna un valor por defecto cuando no produce uno aceptable. Retorna (o tiene un parámetro extra) un explicito estatus del valor, para ser inspeccionado después de cada llamada. Pasar una cláusula para el manejo de errores, para ser llamado en caso de algún problema. El manejo de excepciones puede ser dinámica o estática. En Java los manejadores de excepciones es estático y el código es más limpio y más legible. Un ejemplo de un lenguaje donde el manejador de excepciones sea dinámico es PL/I utilizando la instrucción ON Exception
5
Manejo de Errores C++, Ada, Java, ML
Los manejadores son léxicamente delimitados por bloques de código protegido. Ej. En C++ y en Java el bloque de código se delimita por las palabras try y catch. try { … } Catch (…) finally Ejemplo de Java:
6
Manejo de Errores Propagación de excepciones
Si una excepción es invocada (a través de C++) Si la excepción no es manejada en la subrutina actual, retorna abruptamente de la subrutina. Retorna abruptamente por cada subrutina en la cadena dinámica de llamadas, hasta encontrar un manejador. Si lo encuentra ejecuta el manejador, entonces continua con la ejecución de código luego del manejador. Si no es encontrada hasta el nivel más superior (programa principal), el programa termina.
7
Manejo de Excepciones Ejemplo en C++ Void f() Void g() { { … … try
} catch(exc) // Manejador de excepciones de tipo exc. Void g() { … h(); } Void h() if (…) throw exc();
8
Manejo de Excepciones Usos de los mecanismos de manejadores de excepciones. Realiza operaciones de recuperación y entonces continua con la operación. Asigna mayor cantidad de memoria. Recupera de errores en un compilador. No se puede recuperar localmente, pero: Tal vez se quiera un manejador local justo para limpiar algunos recursos. Invocar la excepción para ser manejada por la autoridad superior. Termina, pero primero imprime una ayuda en el mensaje de error.
9
Manejo de Excepciones Ventajas Manera uniforme de manejar los errores.
Maneja los errores exactamente como queremos, sin chequear para ellas explícitamente todo lo que debe ocurrir. Documentar la subrutina – especifica que excepciones pueden ser invocadas por una subrutina (el usuario de la subrutina tal vez quiera atraparlos).
10
Definición de Excepciones
Excepciones parametrizadas: el código con el cual es invocado la excepción, pueden pasar información adicional con ellas. Ej. - C++ class duplicate_int_set { int dup; } Throw duplicate_in_set(d); - Ada, Common Lisp: las excepciones son solo etiquetas no existe otro tipo de información que el nombre de la excepción.
11
Propagación de Excepciones
C++ (Excepciones predefinidas) try { … // Bloque de código protegido } catch(end_of_file) { // Derivado por un error de E/S … } catch(io_error e) { // Cualquier io_error aparte del end_of_file …} catch(…) { // Todas las demás excepciones El manejador coincide con la excepción si el nombre de una clase de la cual la excepción es derivada. Se puede declarar un objeto exception (e), el cual provee información adicional a la excepción.
12
Implementación de Excepciones
¿Cómo podemos hacer para mantener una traza de manejadores? Mantener una pila separada de manejadores Cuando se ingrese una subrutina, en el prologo, colocar todos sus manejadores de excepciones en la pila de excepciones. Cuando se retorne de una subrutina, (en el epilogo) sacar todos los manejadores de excepciones de la pila de excepciones. Problemas Sobrecarga (overhead) a tiempo de ejecución. Si una excepción es invocada hay que: Chequear si alguno de los manejadores actuales (tope de la pila) coincide con la excepción En caso afirmativo lo ejecuta. Sino, realiza el epilogo de la subrutina y retorna, entonces chequea nuevamente el llamador. El problema que se presenta es una sobrecarga en tiempo de ejecución, ya que se mantiene las excepciones en la pila de manejadores, aun cuando ninguna de las excepciones ocurra. Más adelante se tratará este tema con más detalles.
13
Implementación de Excepciones
Solución Alternativa A tiempo de compilación, construir una tabla de manejadores. Cada entrada tendrá dos campos las cuales corresponden a: La dirección de código protegido. La dirección del manejador correspondiente. Si una excepción es invocada: Realizar una búsqueda binaria para la dirección del bloque actual en la tabla. Si se encuentra y el manejador corresponde con la excepción, se ejecuta el manejador. De los contrario retorna y repite el código del llamador. La solución más obvia de la implementación de excepciones es hacer una lista enlazada a la pila de manejadores. Cuando el manejador entra a el código protegido el manejador de ese bloque es agregado a la cabeza de la lista, si la excepción es invocada a tiempo de ejecución se busca el manejador en la lista y se ejecuta. El problema con esta implementación es que se produce overhead a tiempo de ejecución. El único propósito de utilizar una lista de manejadores es saber cual de ellos se encuentra activado. Una solución propuesta es construir una tabla a tiempo de compilación con el nombre del manejador y con apuntador a código donde se encuentre el mismo, esta solución elimina el overhead a tiempo de compilación que se producía con la solución anterior.
14
Implementación de Excepciones
Propiedades Si una excepción ocurre: Aumenta el costo a tiempo de ejecución en la solución previamente planteada (búsqueda por la dirección en la tabla). En casos comunes (donde ninguna excepción ocurre): El costo a tiempo de ejecución es cero (la tabla construida a tiempo de compilación). El costo de la segunda implementación que se comento aumenta por el factor logarítmico en el número de manejadores del programa, pero este costo se paga cuando la excepción ocurre, en caso contrario el costo es cero. Para lenguajes como Java en el cual los fragmentos de código son compilados independientemente, podemos emplear un mecanismo híbrido en el que el compilador crea una tabla separada por cada subrutina y en cada marco de pila contiene un apuntador a la tabla correspondiente.
15
Iteradores Definición: control de abstracción que permite enumerar los elementos de un tipo abstracto de datos. Recorrer un arreglo Calcula el máximo número de elementos, promedio de todos los elementos, despliega los elementos, etc. Recorres un árbol Calcula el máximo número de nodos, saca el promedio de todos los nodos, cuenta el número de nodos, etc. En el recorrido de los árboles, con un código recursivo se puede hacer más compleja y no es conveniente en cada llamada que se hace. Por otra parte es recomendable solamente utilizar algo similar a un ciclo for que esconde los detalles
16
Iteradores for i in from_to_by (first, last, step) do … end
from_to_by = iter ( from, to, by: int ) yields (int) i: int := from if by then while i = to do yield i i + := by end else while i = to do i +:= by end from_to_by yield Retorna el contro el valor actual de i. La siguiente iteración continua Desde el punto donde quedo.
17
Iteradores Icon: son iteradores que son llamados generadores.
La enumeración es controlada por un ciclo en icon every i:= first to last by step do { … } -… to … by … generador construido con notación infija Otros generadores de tipo Icon (usualmente operan sobre cadena de caracteres) Extendiendo un poco este punto para que quede claro, daremos otro ejemplo. Icon – imprime todas las posiciones de una cadena de caracteres “s” donde se encuentre un espacio en blanco. every i:= 1 + upto (‘ ’ , s) do { write (i) } Que también puede ser escrito como: every write (1 + (‘ ’, s)) En el primer ejemplo es más legible que el segundo. Cualquier usuario puede definir una subrutina en Icon, puede ser un generador. Necesita utilizar suspend expr instaciado de return expr. Suspend es el equivalente de yield en Clu. Suspend retorna el control de un iterador (con el valor generado), pero mantiene el estado para la próxima iteración. find (substr, str) – genera las posiciones en la palabra str donde substr aparezca. upto (chars, str) – genera la posición en la palabra str donde cuqlquier carácter chars aparezca.
18
Iteradores Euclid – son aquellos iteradores que son emulados.
La sintaxis de un ciclo for permite usar un módulo generador El módulo debe exportar Las variables llamadas value y stop Un procedimiento llamado next Ejemplo de recorrido de un árbol: for n in TreeIterModule loop … end loop El cual es equivalente a: begin var ti: TreeIterModule loop exit when ti.stop n:= ti.value … ti.next end loop end
19
Corrutinas Definición: rutinas que no están subordinadas sino que trabajan cooperativamente, pero se ejecutan una a la vez. Transfieren el control de una a la otra explícitamente por nombre. Código de la Corrutina A Código de la Corrutina B Ejemplo: Como se muestra en el ejemplo expuesto el control de una corrutina no pasa a otra a menos que sea invocado, y se ejecuta una a la vez. Las corrutinas no deben ser confundidas con los hilos de ejecución que ya existen diferencias entre ambas mencionadas anteriormente. Transfer B Transfer A Transfer B Trasnfer A
20
Corrutinas Implementación
Clausura: la dirección a código y una referencia al ambiente. Transfer: salto a través de un no-local goto, después de grabar el estado actual. Lenguajes que proveen corrutinas: Simula, Modula-2 Son útiles para la implementación de: - Iteradores - Simulación de eventos discretos - Threads - Juegos de 2 personas - Servers La principal diferencia entre dos abstracciones es que su continuación es constacte, o sea, que no cambian una vez creadas, mientras que las corrutinas cambia cada que se ejecuta. Las corrutinas son usadas para implementar iteradores y threads, estos últimos aparecen en una gran cantidad de lenguajes, como en Algol 68, Modula (1), Modula-3 Ada, SR, Occam y Java. También es comúnmente encontrada fuera del lenguaje pero proveída en librerías. Para poder ser trasferido de una corrutina a otra en tiempo de ejecución es necesario cambiar el program counter PC, la pila y los registros del procesador, estos cambios están encapsulados en la operación de transfer y todo ellos es salvado. La estructura de datos que representa la corrutina o el thread es llamado bloque de contexto.
21
Corrutinas Ejemplo de un programa protector de pantalla
Muestra una imagen En el fondo, chequea el disco para conseguir archivos maliciosos o corruptos. Sin corrutinas (loop) Actualizar una porción de la imagen de pantalla. Ejecuta el siguiente paso del chequeo de archivos del sistema. Problemas No todas las tareas pueden ser divididas en pasos. El código adquiere una estructura compleja Para explicar un poco más sobre los problemas que se expusieron en la lámina, cuando hablamos de estructura compleja nos referimos a anidamientos de ciclos, cuya legibilidad no es tan inmediata, al tener esta estructura a nivel de operacional dificulta guardar/restaurar el estado. Otro problema que se puede presentar usando subrutinas regulares, cuando se realiza el retorno, toda la información en el frame activo se pierde. En una simple corrutina el bloque de contexto contiene un solo valor, el sp (stack pointer) de la mas reciente transfer efectuado (existen variaciones según el lenguaje). Existe una variable global llamada current_coroutine contiene un apuntador a el bloque del contexto de la corrutina que esta corriendo, este apuntador permite al tranfer encontrar el lugar donde guardo el viejo sp.
22
Iteradores como Corrutinas
Es fácil implementar iteradores con corrutinas for i from_to_by (first, last, step) do … end El compilador traduce esto como: It:= new form_to_by(first, last, step, i, done, current_coroutine) While not done do trasfer(it) destroy (it)
23
Iteradores como Corrutinas
La corrutina que implemento el iterador from_to_by es: Coroutine form_to_by (from, to, by: int, ref i:int, ref done : bool, caller: coroutine) i:= from if by then done := from = to detach loop i +:= by done := i = to transfer(caller) --yield i else
Presentaciones similares
© 2025 SlidePlayer.es Inc.
All rights reserved.