La descarga está en progreso. Por favor, espere

La descarga está en progreso. Por favor, espere

Control en subprogramas. Pelczer Ildikó Judit. Díaz Vera Alexei Eleusis. García Cárdenas Egar Arturo.

Presentaciones similares


Presentación del tema: "Control en subprogramas. Pelczer Ildikó Judit. Díaz Vera Alexei Eleusis. García Cárdenas Egar Arturo."— Transcripción de la presentación:

1 Control en subprogramas. Pelczer Ildikó Judit. Díaz Vera Alexei Eleusis. García Cárdenas Egar Arturo.

2 1. Subprogramas o Rutinas.

3 Los subprogramas o rutinas son unidades más pequeñas en las que se puede dividir un programa. Procedimientos: Son rutinas que no regresan valor o regresan un valor vacío. Funciones: Son rutinas que regresan un valor.

4 Las rutinas se introducen mediante una declaración. Las rutinas son descritas mediante una definición. Las rutinas se activan mediante una invocación o llamada.

5 /*suma es un función que calcula la suma de los primeros n naturales 1+2+3+...+n; se supone que n es positivo*/ int suma(int n) { int i,s; s=0; for(i=1;i<=n;i++) s+=i; return s; }

6 Alcance: Los elementos que pueden ser accesados por la rutina. Local: Los elementos declarados dentro de ella. No-local: Los elementos declarados independiente de ella. Global: Los elementos visibles para todas las rutinas.

7 int A(int x, int y);//declaración de una función con dos //parámetros de tipo int con valor de //regreso int; A es visible a partir de este punto float B(int z) //Definición de la función B; //B es visible a partir de { //este punto int w,u;... w=A(z,u); //se llama a A que en este punto ya es visible... }; int A(int x, int y) //esta es la definicón de A { float t;... t=B(x); //B es visible en este punto... }

8 Nombre: Se usa un encabezado que contiene: El nombre del subprograma. Los parámetros. El valor de regreso (si hay). Tipo: Se define por medio de la firma.

9 L-value: Es el área de memoria donde se encuentra el código de la rutina. R-value: Es una activación de la rutina.

10 int(*ps)(int); //esta línea declara que ps es un apuntador a //una rutina de tipo entero en C ps=&suma; //esta línea hace que ps apunte la rutina suma int i=(*ps)(5); //invoca el r-value de la rutina hacia la que //apunta ps

11 La representación de una rutina en ejecución se llama instancia de la rutina, está compuesta por: Bloque de código: Registro de Activación: Contiene información necesaria para la ejecución. Espacio para las variables locales. Un apuntador de regreso.

12 Referencia ambiental de una instancia de una rutina: Consiste de todos los elementos locales y no-locales relacionados con los objetos dentro del registro de activación. Local. No-local.

13 Parámetros formales: Son los que aparecen en la declaración de la rutina. Parámetros reales: Son los que aparecen en la invocación a la rutina.

14 Procedure Ejemplo(A:T1;B:T2:=W;C:T3); --los parámetros A,B,C son de tipo T1, T2 y T3 --respectivamente; --un valor por omisión de B esá dado por W --las siguientes llamadas son legales si X, Y, Z y C son de --tipo T1, T2 y T3 respectivamente Ejemplo(X,Y,Z); --esta es asociación posicional Ejemplo(X,C=>Z); --X está asociado posicionalmente, --B toma el valor por omisión y --Z está asociado con C explícitamente Ejemplo(C=>Z,A=>X,B=>Y);

15 1.1. Rutinas Genéricas.

16 Son machotes (templates) desde los cuales de una rutina general se pueden generar varias rutinas específicas.

17 template void swap(T& a, T& b) { T temp = a; a = b; b = temp; } void main() { int a=5, b=7; Complex c(1,0), d(4,5); swap(a,b); swap(c,d); }

18 1.2. Alias y sobrecarga.

19 Sobrecarga: Un mismo nombre para distintas entidades. Una ocurrencia especifica del nombre debe proveer suficiente información para determinar la entidad de manera única. Alias. Dos o más nombres se refieren a la misma entidad.

20 a=b+c+b(); //ejemplo de sobrecarga a=b()+c+b(i); //otro ejemplo de sobrecarga int x=0; //en este ejemplo i y j se refieren a x int& i=x; // y cualquier modificación que se haga a int& j=x; //cualquiera de los tres afectará a los tres

21 1.3. Un procesador semántico abstracto.

22 SIMPLESEM es un procesador semántico abstracto, consta de: Un apuntador de instrucción IP. Una memoria dividida en dos partes C(Código) y D (Datos). C[X] y D[X] son los valores almacenados en las celdas X de C y D. X es un l-value y D[X] un r-value.

23 Instrucciones: set dirección, valor jump dirección jumpt dirección, condición halt Entrada –salida: write y read junto con la instrucción set. Se permiten usar operadores aritméticos y lógicos para calcular los distintos parámetros de las instrucciones.

24 2. Estructura de tiempo de ejecución.

25 De acuerdo a los requerimientos de memoria se tienen los siguientes tipos de lenguajes: Lenguajes totalmente estáticos. Lenguajes basados en pilas o stack. Lenguajes dinámicos.

26 2.1. Lenguajes con un solo enunciado simple.

27 Hay solo una rutina principal que contiene la declaración de lo datos y las instrucciones que los manipulan. Solo se tiene un registro de activación.

28 main() { int i,j; get(i,j); while(i!=j) if (i>j) i-=j; else j-=i; print(i); }

29

30 Ejemplo

31 2.2. Lenguajes con rutinas simples.

32 Se tiene un conjunto de declaraciones globales. Un conjunto de declaraciones de rutinas. Una rutina principal. Las rutinas no están anidadas, no se pueden llamar a si mismas y no regresan valor.

33 El tamaño de los registros de activación se conoce en tiempo de compilación. Los registros de activación se asignan en memoria en tiempo de compilación. El registro de activación consta de: Dirección de regreso. Espacio para las variables locales. En compilación a cada variable se le asocia un desplazamiento.

34 Varias implementaciones de FORTRAN y COBOL siguen este modelo.

35 int i=2, j=2, k=3; alfa() { int i=4, l=5; i+=k+1; print(i); print(j); print(k); print(l); } beta() { int k=6; i=j+k; alpha(); print(i); print(j); print(k); } main() { beta(); print(i); print(j); print(k); }

36

37 Ejemplo

38 2.3. Funciones recursivas.

39 La recursión consiste en: La habilidad que tienen los programas para llamarse a si mismos. Directa. Indirecta. La capacidad de regresar valores.

40 Se usa una estructura de Stack. Para cada invocación de la rutina se crea un nuevo registro de activación. Todas las instancias comparten el mismo código. A cada variable se le asocia un desplazamiento en el correspondiente registro de activación.

41 Se tiene que guardar la dirección del registro de activación actual, para enlazar las variables con su dirección. Al momento de regreso se tiene que recuperar el registro de activación de la rutina invocadora. En el registro de activación se coloca un espacio para la dirección de regreso y otro para la dirección base de la rutina invocadora, a este último se le llama liga dinámica.

42 La cadena de ligas dinámicas a partir del último registro de activación es llamada la cadena dinámica. Para colocar un nuevo registro de activación es necesario conocer la primera celda libre en el Stack. La rutina invocadora extiende su registro de activación para reservar el espacio para el valor de regreso.

43

44 int n; int fact(int n) { if (n>1) return n*fact(n-1); else return 1; } main() { get(n); if (n>=0) print(fact(n)); }

45 Ejemplo

46 2.4. Estructura de bloques.

47 Sirve para controlar el alcance y tiempo de vida de las variables. Divide el programa en unidades más pequeñas. Dos bloques pueden ser ajenos o anidados.

48 2.4.1. Anidamiento por enunciados compuestos.

49 Las variables localmente declaradas en un enunciado compuesto son visibles en el y en los enunciados compuestos inmersos. Las declaraciones internas enmascaran a las externas con el mismo nombre. Se define también el tiempo de vida de las variables locales, la variable se enlaza a un espacio en memoria mientras el bloque está activo.

50 Una estructura de bloque se puede describir por medio de un árbol sintáctico de anidamiento. En el registro de activación de una rutina se asigna espacio suficiente para todas las variables. Dos bloques ajenos no pueden estar activos al mismo tiempo. Se puede usar el mismo espacio en memoria para variables distintas en bloques ajenos (overlay).

51 int f() { // bloque 1 int x,y,w; while(x!=0) { // bloque 2 int x,z; while(...) { // bloque 3 int y; } // fin del bloque 3 if(...) { // bloque 4 int x,w; } // fin del bloque 4 } // fin del bloque 2 if (...) { // bloque 5 int a,b,c,d;... } // fin del bloque 5 } // fin del bloque 1

52

53

54 Ejemplo

55 2.4.2. Anidamiento por rutinas localmente declaradas.

56 Las rutinas pueden ser declaradas dentro de otras. Una rutina puede ser usada en el bloque donde ella es declarada y en los bloques inmersos. Incluyendo el cuerpo de la misma rutina. Se puede definir un árbol de anidamiento estático para las subrutinas.

57 Las variables locales se enlazan en tiempo de ejecución al registro de activación actual. Las variables globales se enlazan en tiempo de compilación. Las variables no locales se tienen que asociar al registro de activación de la rutina envolvente. Para ello se usa un apuntador llamado liga estática.

58 La secuencia de ligas estáticas empezando en el ultimo registro de activación es llamada cadena estática. Las referencias a variables no-locales se resuelven por medio de la cadena estática. En compilación se puede resolver la distancia en la cadena estática entre uso de una variable y su declaración.

59 En compilación a cada variable se le puede asociar un par y con un método adicional se resuelve la referencia. Para instalar la liga estática en el registro de activación de una rutina, se sigue una distancia D la liga estática a partir de la rutina invocadora, donde D es la distancia entre la llamada de la rutina invocada y su declaración.

60 int x,y,z=0; // bloque 0 f1() { // bloque 1 int t,u; f2() { // bloque 2 int x,w; f3() { // bloque 3 int y,w,t;... } // fin del bloque 3 x=y+t+w+z; }... // fin del bloque 2 } // fin del bloque 1 main() { // bloque 4 int z,t;... } // fin del bloque 4 // fin del bloque 0

61

62

63 Ejemplo

64 PROGRAM anomaly(input,output) PROCEDURE S; {1} BEGIN writeln("wrong one"); END; PROCEDURE T; {Missing: PROCEDURE S; FORWARD; here} PROCEDURE U; BEGIN S {2} END; PROCEDURE S; {3} BEGIN writeln("right one"); END; BEGIN U END; BEGIN T; END.

65 2.5. Comportamientos más dinámicos.

66 La intención es proveer mecanismos para el manejo de variables cuyos requerimientos de memoria no se conocen en compilación.

67 2.5.1. Registros de activación cuyo tamaño se conoce en la unidad de activación.

68 El tamaño de las variables no se conoce en tiempo de compilación, como es el caso de los arreglos dinámicos. Un descriptor es un área de memoria incluida en el registro de activación, que contiene alguna información de la variable y un apuntador a la dirección en memoria donde estan sus datos.

69 En compilación se reserva espacio para los descriptores en el registro de activación. El registro de activación se extiende para incluir espacio para la variable. El descriptor apunta a esta área extendida.

70 type VECTOR is array (INTEGER range<>);... A: VECTOR(0..N); B: VECTOR(1..M);

71

72 2.5.2. Alojamiento completamente dinámico de memoria.

73 Los datos se pueden colocar en memoria de manera explicita a través de ciertas instrucciones. En la mayoría de los lenguajes esto es por medio de apuntadores. Los datos no se pueden colocar en el Stack, así que se colocan en un área de memoria especial llamada Heap. La dirección del dato se enlaza a una variable en el registro de activación.

74 struct node { int info; node* left; node* right; }... node* n = new node;

75

76 2.6. Lenguajes dinámicos.

77 Adoptan reglas dinámicas en lugar de estáticas.

78 2.6.1. Tipos dinámicos.

79 El tipo de variable y sus métodos de acceso no se pueden determinar en compilación. El tipo de la variable se almacena en el registro de activación, junto con un apuntador a su descriptor. Los descriptores son guardados en el heap. El valor también es guardado en el heap.

80

81 2.6.2. Alcance dinámico.

82 - Static versus dynamic, example: A: integer; -global variable Procedure first A=1; Procedure second A: integer; --local varibale First() A:=2; If read_integer()>0 Second() Else First() Write_integer(a)

83 El alcance de las variables depende de la cadena dinámica. No se tiene liga estática en los registros de activación. No es posible determinar estáticamente el tipo y el contexto de las variables. Por lo cual los programas son más dificiles de leer. Las variables locales pueden ser vistas por cualquier subprograma, por lo cual los programas son menos confiables. Los subprogramas invocados heredan los contextos de los invocadores, lo cual permite una comunicación entre las unidades del programa.

84 Procedure big; Var x : integer; Procedure sub1; Begin :::x end; -sub1 procedure sub2; var x: integer; begin ::::: end; - sub2 begin -big ::: end -big

85 Acceso profundo: La cadena dinámica es recorrida hasta encontrar la instancia de la variable. Se requiere guardar en el registro de activación el nombre de la variable.

86 Procedure c; Integer x,z; Begin X:=u+v;...... end; Procedure b; Integer w,x; Begin.... End; procedure A; integer v,w; begin :::::: end; program main; integer u,v; begin ::: end; Suppose the following sequence: Main calls A, A calls A, A calls B, B calls C.

87

88 Acceso superficial: Los nombres de las variables no se almacenan en el registro de activación. Solo hay una versión visible de una variable en un momento dado. Para implementarlo se puede tener un stack para cada nombre de variable o bien una tabla central de ambiente de referencia.

89 Stack separado para cada variable: Cuando una nueva variable es creada, se coloca en su respectivo stack. Cuando la rutina termina, la variable se retira del stack. Este método permite referencias rápidas a variables, pero el mantenimiento del stack es costoso.

90

91 Tabla central de ambiente de referencia: Contiene una lista de todos los identificadores. En compilación a cada variable se le puede asociar un desplazamiento en esta tabla. Cada identificador esta asociado con su dirección activa en curso. Una bandera indica si un identificador está activo o no.

92 Para cada activación la tabla debe reflejar el ambiente de referencia. Se necesita un stack oculto para recuperar los ambientes de referencia previos. Las referencias a variables son rápidas pero el mantenimiento del stack oculto es costoso.

93

94 El acceso profundo es eficiente al invocar subprogramas, pero las referencias a variables no locales son costosas. En el acceso superficial pasa lo contrario.

95 2.7. Paso de parámetros.

96 Existen dos formas en las que un subprograma puede tener acceso a los datos: Acceso directo de variables no-locales. Transmisión de parámetros. Los datos transmitidos como parámetros son ingresados mediante nombre locales para el subprograma.

97 2.7.1. Datos como parámetros.

98 Los parámetros formales están caracterizados por uno de los siguientes modelos semánticos: In-mode: Pueden recibir datos del parámetro real. Out-mode: Pueden transmitir datos hacia el parámetro real. Inout-mode: Pueden hacer ambos. Estos modelos describen el comportamiento del parámetro

99 Existen dos modelos conceptuales de cómo se efectúan las transferencias de datos en las transmisiones: Un valor real es desplazado físicamente (hacia el llamado, hacia el llamador, o ambos). Se usa una transferencia de la transmisión de acceso (usualmente un apuntador).

100 2.7.1.1. Paso por copia.

101 Se divide en tres subtipos: Paso por valor. Paso por resultado. Paso por valor-resultado.

102 2.7.1.1.1. Paso por valor.

103 El valor del parámetro real se usa para iniciar el valor del parámetro formal. La semántica es in-mode. Se puede implementar por medio de un desplazamiento físico. Desventaja: Si se usa un desplazamiento físico, las operaciones de almacenamiento y de desplazamiento pueden ser costosas si el parámetro es grande.

104 Ventaja: Los parámetros reales no sufren cambios, lo cual de alguna manera los protege de resultados no deseados.

105 Void swap (int a, int b) { int temp=a; a=b; b=temp; } We make the call: swap(c,d); The actions can be described like: a=c; b=d; move second parameter value in temp=a; a=b; b=temp;

106 2.7.1.1.2. Paso por resultado.

107 El parámetro formal actúa como variable local y justo antes del regreso su valor es transferido al parámetro real. La semántica es out-mode. Para implementarlo se requiere almacenamiento suplementario y operaciones de copia.

108 Desventaja: Puede haber colisión de parámetros, el orden en el que los parámetros reales están asignados determina su valor. La implementación debe ser capaz de elegir entre dos diferentes tiempos para evaluar las direcciones de los parámetros reales: al tiempo de la llamada o al tiempo de su regreso. Esto puede producir diferentes resultados.

109 2.7.1.1.3. Paso por valor- resultado.

110 El valor del parámetro real es usado para iniciar el parámetro formal. Justo antes de regresar el valor es transferido al parámetro real. La semántica es inout-mode. Desventaja. Requiere de múltiples almacenamientos para parámetros y de tiempo para copiar valores. El orden de asignación de los parámetros puede ser una causa de problemas.

111 f(int x by val-result, int y by val-result) { x=0; y++; } main() { int i,j; int a[5]={1,2,3,4,5}; get(i,j); f(a[i],a[j]); print(a); }

112 Ejemplo

113 2.7.1.2. Paso por referencia.

114 Transmite un camino de acceso al parámetro real. El parámetro real es compartido entre invocado e invocador. La semántica es inout-mode. Ventaja. Es eficiente en términos de espacio y tiempo, ya que no se requiere duplicar el espacio ni efectúa copia alguna.

115 Desventaja. El acceso a los parámetros formales será más lento pues se requiere de más nivel de direccionamiento indirecto que cuando los valores de los datos son transmitidos. Pueden además ocurrir cambios involuntarios en los parámetros actuales. Pueden crearse alias, ya que los caminos de acceso a los subprogramas abiertos estan abiertos.

116 swap (int& a, int& b) { int temp; temp=a; a=b; b=temp; } main() { int i; int a[5]={1,2,3,4,5}; get(i); swap(i,a[i]); print(a); }

117 f(int& x, int& y) { x=0; y++; } main() { int i,j; int a[5]={1,2,3,4,5}; get(i,j); f(a[i],a[j]); print(a); }

118 Ejemplo

119 2.7.1.3. Paso por nombre.

120 El parámetro formal es sustituido textualmente por el parámetro real correspondiente. La semántica es inout-mode. Se implementan mediante un thunk, que es un proceso que evalua la referencia adecuada. Ventaja: Es sumamente flexible.

121 Desventaja: El proceso es lento. El concepto de ligado tardío en el que está basada la Llamada por nombre es usado en poliformismo dinámico. La evaluación perezosa es otro mecanismo útil (es una forma de ligado tardío): El proceso de evaluación de las partes del código se realiza solo cuando la evaluación es necesaria.

122 int c=0; swap (int a by name, int b by name) { int temp; temp=a; a=b; b=temp; c++; } main() { int i; int a[5]={1,2,3,4,5}; int c,d; get(i); swap(i,a[i]); print(a); get(c,d); swap(c,d); print(a); }

123 Ejemplo

124 Paso por referencia es el estándar en FORTRAN. Paso por nombre es el estándar en ALGOL 60, pero es opcional el paso por valor. SIMULA 67 permite paso por valor, referencia y por nombre. C++, PASCAL y MODULA-2 permiten pasar por valor o por referencia.

125 Var w,x,y,z:integer; Procedure sub(a,b,c,d:integer); Var i: integer;... end; begin :::: call sub(w,x,y,z) {pass w by value, x by result, y by value-result, z by reference} ::: end;

126

127 2.7.2. Rutinas como parámetros.

128 Las rutinas pueden ser pasadas como parámetros, se dice que las rutinas son objetos primarios. Se tienen 3 opciones para elegir el ambiente de referencia de una rutina parámetro. 1. El ambiente del invocador. 2. El ambiente del bloque en donde está declarada. 3. El ambiente del bloque que incluye la instrucción de llamada que la transmite.

129 La primera opción se llama enlace superficial. La segunda opción se llama enlace profundo. La tercera opción no ha sido utilizada.

130 Enlace profundo: Se utiliza en los lenguajes estáticos estructurados por bloques. Se usa para crear una representación explícita del ambiente de referencia. Se comporta como si la rutina pasada como parámetro se ejecutara directamente. Al pasar una rutina como parámetro también se pasa su liga estática.

131 Para pasar la liga estática de A a B en una unidad U: Si A es un parámetro formal en U, se pasa la liga estática previamente pasada a U. Si A es no-local o localmente visible en U, se pasa un apuntador al registro de activación que esta a d pasos en la cadena estática originada en la invocadora. Donde d es la distancia entre la declaración de A y el punto donde es pasada como parámetro.

132 Enlace superficial: El ambiente de referencia es determinado por la cadena dinámica, empezando por el ultimo registro de activación. Se usa en algunos lenguajes con alcance dinámico.

133 int u,v; a() { int y; } b (routine x) { int u,v,y; c() {... y=....... }; x(); b(c);.... } main () { b(a); }

134

135 3. Bibliografía.

136 Programming language Concepts. Carlo Ghezzi, Mehdi Jazayeri. Tercera Edición, 1998. Ed. Wiley. Programming Languages. Design and implementation. Terrence W. Pratt, Marvin V. Zelkowitz. Cuarta Edición, 2001. Prentice Hall.

137 Concepts of programming languages. Robert W. Sebesta. Programming language pragmatics. Michael L. Scott.


Descargar ppt "Control en subprogramas. Pelczer Ildikó Judit. Díaz Vera Alexei Eleusis. García Cárdenas Egar Arturo."

Presentaciones similares


Anuncios Google