Você está na página 1de 178

Gua de lectura para la materia

Paradigmas de la Programaci
on

Licenciatura en Ciencias de la Computacion


FaMAF-UNC

2016
Nota de descargo

Esta gua es un documento de apoyo a la materia Paradigmas de la Programacion de


la Licenciatura en Ciencias de la Computacion de la Facultad de Matematica, Astronoma
y Fsica de la Universidad Nacional de Cordoba.
No es un texto autocontenido ni suficiente para el estudio y aprobacion de la
materia, sino que debera ser complementado con la lectura del material bibliografico reco-
mendado, principalemente el libro Concepts in Programming Languages de John Mitchell,
la asistencia a clases, la realizacion de los ejercicios practicos y los proyectos de laboratorio.
El contenido de este apunte esta fuertemente basado en el libro Concepts in Programming
Languages.
Las condiciones de aprobacion de la materia incluyen la presentacion en tiempo y forma
de los proyectos de laboratorio y la aprobacion de los examenes teoricos. Para informacion
sobre el cursado y condiciones de aprobacion y promocion vigentes, consultar la pagina de
la materia: http://cs.famaf.unc.edu.ar/materias/paradigmas/.

1
Indice general

1. Introduccion y Motivaci on 4
1.1. Nuestro objeto de estudio . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2. Cuestiones fundamentales . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.3. Objetivos del curso Paradigmas de la Programacion . . . . . . . . . . . . 4
1.4. Para que sirve esta materia . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.5. Estructura de este apunte . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

2. Qu e es y que puede hacer un lenguaje de programaci on 6


2.1. Sintaxis y semantica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.2. Alcance de los lenguajes de programacion . . . . . . . . . . . . . . . . . . . 6
2.3. Sintaxis a traves de gramaticas . . . . . . . . . . . . . . . . . . . . . . . . 7
2.4. Semantica operacional vs. lambda calculo o denotacional . . . . . . . . . . 9
2.5. Para saber mas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.6. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

3. Como funcionan los lenguajes de programaci on 16


3.1. Estructura de un compilador . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.2. Estructuras de datos de bajo nivel . . . . . . . . . . . . . . . . . . . . . . . 16
3.2.1. Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.3. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

4. Estructura en bloques 19
4.1. Codigo estructurado vs. codigo spaghetti . . . . . . . . . . . . . . . . . . . 19
4.2. Estructura de bloque . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
4.3. Activation records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
4.3.1. Detalle de ejecucion de un activation record . . . . . . . . . . . . . 25
4.4. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

5. Control de la ejecuci on 30
5.1. Funciones y procedimientos . . . . . . . . . . . . . . . . . . . . . . . . . . 30
5.2. Pasaje de parametros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
5.2.1. Semantica de pasaje por valor (call-by-value) . . . . . . . . . . . . . 31
5.2.2. Semantica de pasaje por referencia call-by-reference . . . . . . . . . 32

2
5.2.3. Sutilezas entre pasaje por valor y pasaje por referencia . . . . . . . 34
5.2.4. Semantica de pasaje por valor-resultado . . . . . . . . . . . . . . . 35
5.2.5. Pasaje de parametros no estricto (perezoso) . . . . . . . . . . . . . 36
5.2.6. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
5.3. Alcance estatico vs. alcance dinamico . . . . . . . . . . . . . . . . . . . . . 42
5.3.1. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
5.4. Recursion a la cola . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
5.4.1. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
5.5. Alto orden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
5.5.1. Funciones de primera clase . . . . . . . . . . . . . . . . . . . . . . . 47
5.5.2. Pasar Funciones a otras Funciones . . . . . . . . . . . . . . . . . . . 48
5.6. Excepciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
5.6.1. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
5.7. Recoleccion de basura (garbage collection) . . . . . . . . . . . . . . . . . . 57
5.7.1. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

6. Paradigma funcional 61
6.1. Expresiones imperativas vs. expresiones funcionales . . . . . . . . . . . . . 61
6.2. Propiedades valiosas de los lenguajes funcionales . . . . . . . . . . . . . . . 63
6.3. Problemas naturalmente no declarativos . . . . . . . . . . . . . . . . . . . 65
6.4. Concurrencia declarativa . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
6.5. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

7. Orientacion a objetos 69
7.1. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69

8. Tipos de datos 99
8.1. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99

9. Programaci on l
ogica 103
9.1. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103

10.Programaci on concurrente 106


10.1. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106

11.Paradigma de scripting 112


11.1. Tipos de lenguajes de scripting . . . . . . . . . . . . . . . . . . . . . . . . 113
11.2. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116

12.Seguridad basada en lenguajes de programaci on 120


12.1. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120

A. Ex
amenes de ediciones anteriores 121

3
Captulo 1

Introducci
on y Motivaci
on

1.1. Nuestro objeto de estudio


Esta materia se llama Paradigmas de la Programacion, pero en otros lugares la pue-
den encontrar bajo el nombre Lenguajes de Programacion, porque el objetivo es aprender
conceptos y propiedades de los lenguajes de programacion.

1.2. Cuestiones fundamentales


Cuales son las atribuciones de un lenguaje de programacion?
Como funcionan los lenguajes de programacion?
Como podemos hablar del significado de un programa?
Como podemos comparar diferentes lenguajes de programacion?

1.3. Objetivos del curso Paradigmas de la Progra-


maci
on
Objetivizar e instrumentar los conceptos involucrados en la constitucion de los len-
guajes de programacion.

1.4. Para qu
e sirve esta materia
En la currcula de un profesional de la computacion, esta materia nos capacita para
analizar, entender y tomar decisiones respecto a las fortalezas y desventajas de los diferentes
lenguajes de programacion en diferentes contextos y para diferentes tareas.
Por ejemplo, para un proyecto en el que la eficiencia sea crtica (p.ej. dispositivos
moviles, grandes vol
umenes de datos) podemos elegir un lenguaje que priorice la eficiencia
aunque requiera mas experiencia de parte del programador, como C. En cambio, si el equipo

4
de desarrollo del software esta compuesto de personas con poca experiencia, sera mejor
elegir un lenguaje que ayude a prevenir los errores humanos, como por ejemplo Python.

1.5. Estructura de este apunte


Este apunte es un breve resumen en castellano de los contenidos de la materia. Esta con-
cebido para acompa nar a la lectura del libro Concepts in Programming Languages de John
Mitchell. Cada captulo del apunte tiene un puntero a las secciones correspondientes del
libro, as como a las filminas correspondientes de clase. Al final de cada captulo se encuen-
tran los ejercicios practicos correspondientes. Tambien se encuentran varias preguntas de
tipo verdadero-falso sobre el contenido del captulo.
Al final del apunte se encuentran examenes parciales y finales de ediciones pasadas de
la materia.
El captulo 2 trata sobre el alcance de los lenguajes de programacion y sus componentes
fundamentales: la forma y el significado. En el captulo 3 describimos como funcionan los
lenguajes de programacion en una computadora.
Despues, el captulo 4 presenta la primera abstraccion en lenguajes de programacion, la
estructura de bloques, y a partir de esa abstraccion nuestra unidad de significado basica:
el activation record. En este captulo damos ejemplos de dos lenguajes con abstraccion
de bloques: Algol en el paradigma imperativo y ML en el paradigma funcional. Estos dos
lenguajes estan en la base de la mayor parte de lenguajes modernos.
En captulo 5 mostramos diferentes construcciones de los lenguajes para manejar de
forma eficiente e intuitiva el flujo de ejecucion de un programa. Vemos las implicaciones
en eficiencia y expresividad que tienen las diferentes construcciones.
En el captulo 7 describimos las abstracciones propias del paradigma de orientacion a
objetos, con ejemplos de C++ y Java.
En el captulo 8 describimos mecanismos para organizar y manipular los datos, jerar-
quizandolos en estructuras de conjuntos y subconjuntos, y como estas estructuras otorgan
mayor seguridad a los lenguajes de programacion.
En el captulo 9 presentamos el paradigma de programacion logica, con su u nico expo-
nente, Prolog. En el captulo 10 presentamos como se introduce en lenguajes de programa-
cion secuenciales la semantica de concurrencia, con ejemplos de concurrencia declarativa
con lenguajes como Scala.
Y finalmente en el captulo 11 mostramos las decisiones de dise no y las concreciones
del paradigma de scripting, con ejemplos de glsjavascript y Perl.
Este apunte esta en construccion y se agradecen comentarios y sugerencias a ale-
many@famaf.unc.edu.ar.

5
Captulo 2

Que es y qu
e puede hacer un
lenguaje de programacion

Un lenguaje de programacion es un lenguaje formal dise nado para realizar procesos


que pueden ser llevados a cabo por maquinas como las computadoras. Pueden usarse para
crear programas que controlen el comportamiento fsico y logico de una maquina o para
expresar algoritmos con precision (fuente: wikipedia).

2.1. Sintaxis y sem


antica
Los lenguajes son sistemas que se sirven de una forma para comunicar un significado.
Lo que tiene que ver con la forma recibe el nombre de sintaxis y lo que tiene que ver con
el significado recibe el nombre de sem antica.
En los lenguajes naturales, como el castellano, el ingles o las lenguas de signos, las
palabras son la forma, y el contenido proposicional de las oraciones es el significado.
En los lenguajes de programacion, que son lenguajes artificiales creados por hombres
(lenguajes formales), la forma son los programas y el significado es lo que los progra-
mas hacen, usualmente, en una computadora. En la definicion de arriba, se ha descrito
lo que los programas como controlar el comportamiento fsico y logico de una maquina.
Un lenguaje de programacion se describe con su sintaxis (que es lo que se puede escribir
legalmente en ese lenguaje) y su semantica (que efectos tiene en la maquina lo que se escribe
en ese lenguaje).

2.2. Alcance de los lenguajes de programaci


on
Mitchell 2.
Para controlar el comportamiento fsico y logico de una maquina usamos algoritmos,
un conjunto de instrucciones bien definidas, ordenadas y finitas que permite realizar algo
de forma inambigua mediante pasos.

6
Las funciones computables son la formalizacion de la nocion intuitiva de algoritmo, en
el sentido de que una funcion es computable si existe un algoritmo que puede hacer el
trabajo de la funcion, es decir, dada una entrada del dominio de la funcion puede devolver
la salida correspondiente.
El concepto de funcion computable es intuitivo, se usa para hablar de computabilidad
sin hacer referencia a ning un modelo concreto de computacion. Cualquier definicion, sin
embargo, debe hacer referencia a alg un modelo especfico de computacion, como por ejem-
plo maquina de Turings, las funciones recursivas, el lambda calculo o las maquinas de
registro.
La tesis de Church-Turing1 dice que las funciones computables son exactamente las
funciones que se pueden calcular utilizando un dispositivo de calculo mecanico dada una
cantidad ilimitada de tiempo y espacio de almacenamiento. De manera equivalente, esta
tesis establece que cualquier funcion que tiene un algoritmo es computable.
Algunas funcion no computable famosas son Halting problem o calcular la Complejidad
de Kolmogorov.

2.3. Sintaxis a trav


es de gram
aticas
Nuestro objetivo con respecto a la sintaxis de los lenguajes de programacion es describir
de forma compacta e inambigua el conjunto de los programas validos en ese lenguaje. El
instrumento formal basico para describir la sintaxis de los lenguajes de programacion son
las gramaticas independientes de contexto. El estandar de facto para gramaticas indepen-
dientes de contexto de lenguajes de programacion es EBNF.
Sin embargo, las gramaticas independientes de contexto no son suficientemente expre-
sivas para describir adecuadamente la mayor parte de lenguajes de programacion. Por esa
razon, en la practica el analisis de la forma de los programas suele incorporar diferentes
mecanismos, entre ellos, el analisis de un programa mediante una gramatica independien-
te de contexto, que se complementa con otros mecanismos para alcanzar la expresividad
propia de una maquina de Turing. El proceso completo de analisis lo realiza el compilador
del lenguaje, como se describe en la seccion 3.1.
No obstante, las gramaticas independientes de contexto describen el grueso de la forma
de los lenguajes de programacion.
Una gramatica de ejemplo clasica es la que genera los strings que representan expresio-
nes aritmeticas con los cuatro operadores +, , , / y los numeros como operandos:
1 < expresion > --> numero
< expresion > --> ( < expresion > )
3 < expresion > --> < expresion > + < expresion >
< expresion > --> < expresion > - < expresion >
1
La historia de la colaboraci
on entre Church y Turing es un ejemplo muy interesante de como dos
individuos geniales quieren y pueden trabajar juntos para potenciarse m utuamente y llegar a alcanzar
logros mucho mayores de los que habran alcanzado cada uno por su lado. Fjense tambien en los estudiantes
a los que dirigi
o Church.

7
5 < expresion > --> < expresion > * < expresion >
< expresion > --> < expresion > / < expresion >
El unico smbolo no terminal en esta gramatica es expresion, que tambien es el smbolo
inicial. Los terminales son {+,-,*,/,(,),numero}, donde numero representa cualquier
n
umero valido.
La primera regla (o produccion) dice que una <expresion> se puede reescribir como
(o ser reemplazada por) un n umero. Por lo tanto, un n umero es una expresion valida. La
segunda regla dice que una expresion entre parentesis es una expresion valida, usando una
definicion recursiva de expresion. El resto de reglas dicen que la suma, resta, producto o
division de dos expresiones tambien son expresiones validas.
Pueden encontrar algunos ejemplos simples de gramaticas independientes de contexto
en https://www.cs.rochester.edu/~nelson/courses/csc_173/grammars/cfg.html
Algunos fenomenos de los lenguajes de programacion no se pueden expresar con na-
turalidad mediante gramaticas independientes de contexto. Por ejemplo es difcil expre-
sar la obligacion de que una variable sea declarada antes de ser usada, o bien descri-
bir la asignacion m ultiple de variables, como podemos hacer por ejemplo en Python:
(foo, bar, baz) = 1, 2, 3.

Pregunta 1:
Piense como sera una gramatica independiente de contexto para expresar la
obligacion de que una variable sea declarada antes de ser usada, o la asigna-
cion m
ultiple de variables. Como es la gramatica resultante? Resulta facil de
pensar, facil de entender?

Las gramaticas que describen los lenguajes de programacion se usan para verificar la
correctitud de un programa escrito en ese lenguaje, mediante un compilador. En un compi-
lador, estas limitaciones se solucionan en parte porque hay modulos de procesamiento del
programa posteriores a la gramatica que tratan algunos de los fenomenos que las gramaticas
no pueden capturar.
Sin embargo, tambien se usan las gramaticas para describir los lenguajes para consumo
humano. En ese uso, uno desea que la gramatica pueda expresar de forma compacta todas
las propiedades relevantes del lenguaje. Para conseguirlo, se suelen usar mecanismos ad-
hoc para aumentar su poder expresivo, como por ejemplo predicados asociados a reglas.
Por ejemplo, si queremos establecer una restriccion de tipos en una determinada regla,
podemos hacerlo de la siguiente forma:
< expresion > --> numero
2 < expresion > --> ( < expresion > )
< expresion > --> < expresion > + < expresion >
4 < expresion > --> < expresion > - < expresion >
< expresion > --> < expresion > * < expresion >
6 < expresion > --> < expresion > / < expresion > ^ < expresion >
es_de_tipo Float

8
< expresion > --> < expresion > div < expresion > ^ < expresion >
es_de_tipo Int
En la practica, las restricciones de tipos las realiza el analisis semantico del compilador.
Para ejercitar este mecanismo de expresividad, resuelvan el ejercicio 2.11.

2.4. Semantica operacional vs. lambda c


alculo o de-
notacional
Hay diferentes maneras de describir y manipular formalmente la semantica de un pro-
grama. Algunas de ellas son el lambda calculo de Church (Mitchell 4.2), la semantica de-
notacional de Strachey & Scott (Mitchell 4.3) y diferentes tipos de semantica operacional.
En nuestro curso vamos a estar usando un tipo de semantica operacional.
La semantica operacional (de peque nos pasos) describe formalmente como se llevan a
cabo cada uno de los pasos de un calculo en un sistema computacional. Para eso se suele
trabajar sobre un modelo simplificado de la maquina.
Cuando describimos la semantica de un programa mediante semantica operacional, des-
cribimos como un programa valido se interpreta como secuencias de pasos computacionales.
Estas secuencias son el significado del programa.
Tal vez la primera formalizacion de semantica operacional fue el uso del calculo lambda
para definir la semantica de LISP que hizo [John McCarthy. Funciones recursivas de
expresiones simbolicas y su computo por maquina, Parte I. Consultado el 2006-10-13.].

Pregunta 2:
Por que puede haber mas de un tipo de semantica operacional, mientras que
el lambda calculo y la semantica denotacional son solo uno?

2.5. Para saber m


as
[John McCarthy. Funciones recursivas de expresiones simbolicas y su computo por
maquina, Parte I. Consultado el 2006-10-13.]

9
2.6. Ejercicios
Seleccione la(s) respuesta(s) correcta(s)
2.1. Una gramatica describe...

a) lo que significan las sentencias de un lenguaje de programacion.


b) las sentencias que forman parte de un lenguaje de programacion.
c) como se pueden escribir sentencias en un lenguaje de programacion.
d) el alfabeto de un lenguaje de programacion.

2.2. Una gramatica independiente de contexto (context-free grammar)...

a) tiene predicados.
b) es inambigua.
c) tiene reglas de reescritura.
d) describe la sintaxis de un lenguaje.
e) es mas expresiva que una gramatica dependiente del contexto.
f) es tan expresiva como una maquina de Turing.

2.3. Se puede expresar con una gramatica libre de contexto la obligacion de declarar una
variable antes de que se use?

a) Verdadero.
b) Falso.

2.4. Se puede expresar con una gramatica libre de contexto la declaracion de variables
con tipo?

a) Verdadero.
b) Falso.

2.5. Se puede expresar con una gramatica libre de contexto la asignacion m


ultiple de
variables?

a) No.
b) S, si se trata de de asignacion en secuencia, y no en espejo.
c) S, si se trata de un n
umero finito de variables.
d) Todas las anteriores.
e) Ninguna de las anteriores.

10
2.6. Una gramatica que puede ofrecer mas de un arbol de derivacion para una misma
expresion es una gramatica...

a) Incorrecta.
b) Ambigua.
c) Dependiente de contexto.
d) Independiente de contexto.

2.7. Dada una gramatica EBNF, como se puede probar si una secuencia de tokens es
valida?

a) Si existe una secuencia de reglas de la gramatica que puedan aplicarse sucesiva-


mente desde el smbolo terminal de forma que su resultado sea la secuencia de
tokens.
b) Si los tokens pertenecen al alfabeto de la gramatica.
c) Si podemos dar un arbol de derivacion de la secuencia.
d) Si la secuencia pertenece al lenguaje de la gramatica.

2.8. La semantica de los lenguajes de programacion debe reflejar formalmente...

a) la intencion del programador.


b) los efectos de un programa en una computadora.

2.9. La semantica operacional...

a) describe el significado de un programa como una secuencia de transiciones de


estado a estado.
b) usa una maquina abstracta para representar el significado de un programa.
c) tiene un conjunto de operaciones para definir la semantica de un programa.

2.10. La semantica denotacional...

a) explica el significado de un programa paso a paso.


b) explica un programa como una funcion (parcial) de estados a estados.
c) son una traduccion de un programa en cualquier lenguaje a cualquier otro len-
guaje.

2.11. En EBNF no podemos expresar lenguajes Turing completos, por esa razon necesita-
mos aumentar el poder expresivo de las gramaticas libres de contexto. Esto se hace
en la implementacion con el paso posterior al analisis sintactico, el analisis semantico.
Informalmente, podemos aumentar una gramatica libre de contexto con predicados.
Escriba una EBNF aumentada con los predicados necesarios para poder expresar:

11
que una variable debe ser declarada antes de ser usada.
asignacion m
ultiple de variables, con un n
umero arbitrario de variables.

2.12. Considerando la asignacion m


ultiple de variables en Python, es Python un lenguaje
independiente de contexto? Justifique su respuesta.

2.13. Cuando se usa una gramatica independiente de contexto para describir un lenguaje
puede suceder que la gramatica resulte ambigua. Cuando decimos que una gramatica
es ambigua? Que problemas presenta la ambig uedad, y como se pueden solucionar?

2.14. Dada la siguiente gramatica:

<e> ::= <n> | <e>+<e> | <e>-<e>


<n> ::= <d> | <n><d>
<d> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

a) Enumere los conjuntos de no terminales y terminales.


b) De 3 ejemplos de expresiones validas en el lenguaje definido por la gramatica.
Grafique los arboles de derivacion correspondientes.
c) Las siguientes secuencias de smbolos: <e>,e-5, e + 6 - A son expresiones vali-
das en el lenguaje? Justifique su respuesta.
d) Es la gramatica definida ambigua? Si lo es, de un ejemplo que pruebe su am-
big
uedad.
e) Si la gramatica es ambigua, que condiciones agregara para desambiguarla?

2.15. De la misma forma que obtuvimos la derivacion de una expresion algebraica usando
la gramatica de dgitos, use la siguiente gramatica:
1 program = PROGRAM , espacio , identificador , espacio ,
BEGIN , espacio ,
3 { asignacion , " ; " , espacio } ,
END . ;
5 identificador = caracter_alfabetico , {
caracter_alfabetico | digito } ;
numero = [ " -" ] , digito , { digito } ;
7 string = " , { todos_caracteres - " } , " ;
asignacion = identificador , " := " , ( numero |
identificador | string ) ;
9 caracter_alfabetico = " A " | " B " | " C " | " D " | " E " | " F " |
"G"
| "H" | "I" | "J" | "K" | "L" | "M"
| "N"
11 | "O" | "P" | "Q" | "R" | "S" | "T"
| "U"

12
| "V" | "W" | "X" | "Y" | "Z" ;
13 digito = " 0 " | " 1 " | " 2 " | " 3 " | " 4 " | " 5 " | " 6 " | " 7 " |
"8" | "9" ;
espacio = ? caracteres_espacio ? ;
15 all caracteres = ? todos_caracteres_visibles ? ;

Para obtener la derivacion de la siguiente expresion:


1 PROGRAM DEMO1
BEGIN
3 A :=3;
TEXT := " Hello world ! " ;
5 END .

2.16. Muestre con un ejemplo que la siguiente gramatica es ambigua, y modifquela para
que se convierta en una gramatica inambigua. Que estrategias puede usar para
hacerlo?
1 < bloque > ::= { < sentencias > }
< sentencias > ::= < sentencias > < sentencia > | < sentencia >
3 < sentencia > ::= < asignacion > | < sentenciaIf > | < bloque >
< sentenciaIf > ::= if ( < expresion > ) < sentencia > |
5 if ( < expresion > ) < sentencia > else
< sentencia >
< asignacion > ::= < variable > := < expresion >
7 < expresion > ::= < variable > | < variable > < operador >
< variable >
< operador > ::= + | - | * | / | < | > | and | or
9 < variable > ::= x | y | z

2.17. De 3 ejemplos distintos de secuencias de caracteres que satisfagan las siguientes


gramaticas. Justifique por que.

a) ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec] [-D


[bind_address:]port] [-e escape_char] [-F configfile]
[-i identity_file] [-L [bind_address:]port:host:hostport]
[-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port] [-R
[bind_address:]port:host:hostport] [-S ctl_path]
[-w local_tun[:remote_tun]] [user@]hostname [command]

b) <postal-trate> ::= <nombre-parte> <calle-trate> <relampagar-parte>


<nombre-parte> ::= <personal-parte> <pasado-nombre> <OPT-jr-parte> <EOL>
| <personal-parte> <nombre-parte>
<personal-parte> ::= <primero-nombre> | <inicial> "."
<calle-trate> ::= <OPT-conveniente-numerico> <casa-numerico>
<calle-nombre> <EOL>
<relampagar-parte> ::= <ciudad-nombre> "," <estado-codigo>
<Relampagar-codigo> <EOL>
<OPT-jr-parte> ::= \Sr." | \Jr." | <romano-numero> | ""

13
c) <sintaxis> ::= <regla> | <regla> <sintaxis>
<regla> ::= <OPT-whitespace> \<\ <regla-nombre> \>\ <OPT-whitespace> "::="
<OPT-whitespace> <expresion> <linea-extremo>
<OPT-whitespace> ::= " " <OPT-whitespace> | "" <!-- "" es la
secuencia vacia, es decir. ningun whitespace -->
<expresion> ::= <lista> | <lista> "|" <expresion>
<linea-extremo> ::= <OPT-whitespace> <EOL> | <linea-extremo> <linea-extremo>
<lista> ::= <termino> | <termino> <OPT-whitespace> <lista>
<termino> ::= <literal> | \<\ <regla-nombre> \>\
<literal> ::= " <texto> " | "" <texto> "" <! -- realmente, el
BNF original no utilizo cotizaciones -->

2.18. Algunos lenguajes de programacion, como Python, usan la tabulacion para definir el
alcance de las variables y los finales de bloque, como en el siguiente ejemplo:
1 a = " mi vieja mula ya no es lo que era "
b = 0
3 while b <= 3:
print a
5 b = b +1

Como sera el tokenizer para reconocer las expresiones de un lenguaje de programa-


cion as? Escriba una gramatica EBNF para este lenguaje.

2.19. Un programa Lisp esta compuesto por una sucesion de expresiones simbolicas deno-

minadas S-expressions o sexp. Estas se definen formalmente como sigue:
1 < s_exp > ::= < atomic_symbol > | < natural > | " ( " < s_exp >
" . " < s_exp > " ) "
< atomic_symbol > ::= < letter > < atom_part > | " : " < atom_part >
3 < natural > ::= { < number >}+
< atom_part > ::= < empty > | < letter > < atom_part > | < number >
< atom_part >
5 < letter > ::= " a " | " b " | ... | " z "
< number > ::= " 1 " | " 2 " | ... | " 9 "
7 < empty > ::= " "

Determine cuales de los siguientes son programas sintacticamente correctos. Derive,


para los que efectivamente sean programas Lisp, el arbol sintactico correspondiente.

a) 2 .
b) a ( b )
c) (3 . 3)
d) (3 2 1)

2.20. En Lisp un dotted pair es un par escrito con un punto (.) separando ambas partes del
mismo, ej: (3 . 3). Por su parte, una lista de Lisp es una concatenacion de dotted

14
pairs que ademas termina en el atomo nil. Por ejemplo (3 . (2 . (1 . nil))) es
una lista con los elementos 3, 2 y 1. Una syntax sugar para esta lista es (3 2 1).
Modifique la gramatica de arriba para que admita esta forma sintactica.

2.21. Un arbol es una estructura de datos que esta formada por nodos, los cuales tienen
asociado un valor y pueden tener cero o mas nodos hijos conectados. Es posible
implementar dicha estructura utilizando solamente listas, de la siguiente forma:

2 6

3 5 7 8
4

[1 [[2 [[3] [4] [5]]] [6 [[7] [8]]]]]

Escriba una EBNF que formalice la estructura de datos descripta.

2.22. De informalmente (explicando con lenguaje cotidiano, sin necesidad de notacion ma-
tematica) la semantica denotacional de estas dos funciones, es decir, explique infor-
malmente que es lo que hacen. Son equivalentes?
1 function foo ( x ) is :
if predicate ( x ) then
3 return foo ( bar ( x ) )
else
5 return baz ( x )

1 function foo ( x ) is :
while predicate ( x ) do :
3 x <-- bar ( x )
return baz ( x )

15
Captulo 3

C
omo funcionan los lenguajes de
programaci
on

3.1. Estructura de un compilador


Mitchell 4.1.1.

3.2. Estructuras de datos de bajo nivel


3.2.1. Variables
Una de las estructuras de datos basicas de los lenguajes de programacion son las va-
riables. Una variable esta formada por una ubicacion en la memoria y un identificador
asociado a esa ubicacion. Esa ubicacion contiene un valor, que es una cantidad o informa-
cion conocida o desconocida. El nombre de la variable es la forma usual de referirse al valor
almacenado: esta separacion entre nombre y contenido permite que el nombre sea usado
independientemente de la informacion exacta que representa.
Los compiladores reemplazan los nombres simbolicos de las variables con la real ubica-
cion de los datos. Mientras que el nombre, tipo y ubicacion de una variable permanecen
fijos, los datos almacenados en la ubicacion pueden ser cambiados durante la ejecucion del
programa.
El identificador en el codigo fuente puede estar ligado a un valor durante el tiempo
de ejecucion y el valor de la variable puede por lo tanto cambiar durante el curso de la
ejecucion del programa. De esta forma, es muy sencillo usar la variable en un proceso
repetitivo: puede asignarsele un valor en un sitio, ser luego utilizada en otro, mas adelante
reasignarsele un nuevo valor para mas tarde utilizarla de la misma manera, por ejemplo,
en procedimientos iterativos.
Diferentes identificadores del codigo pueden referirse a una misma ubicacion en memo-
ria, lo cual se conoce como aliasing. En esta configuracion, si asignamos un valor a una
variable utilizando uno de los identificadores tambien cambiara el valor al que se puede

16
acceder a traves de los otros identificadores.
Veamos un ejemplo:
x : int ;
2 y : int ;
x : = y + 3;
En la asignacion, el valor almacenado en la variable y se a nade a 3 y el resultado se
almacena en la ubicacion para x. Notemos que las dos variables se utilizan de manera
diferente: usamos el valor almacenado en y, independientemente de su ubicacion, pero en
cambio usamos la ubicacion de x, independientemente del valor almacenado en x antes de
que ocurra la asignacion.
La ubicacion de una variable se llama su L-valor y el valor almacenado en esta ubicacion
se llama el R-valor de la variable.
En ML, los L-valores y los R-valores tienen diferentes tipos. En otras palabras, una
region de la memoria asignable tiene un tipo diferente de un valor que no se puede cambiar.
En ML, un L-valor o region asignable de la memoria se llama celda de referencia. El tipo
de una celda de referencia indica que es una celda de referencia y especifica el tipo de valor
que contiene. Por ejemplo, una celda de referencia que contiene un n umero entero tiene
tipo int ref.
Cuando se crea una celda de referencia, se debe inicializar a un valor del tipo correcto.
ML no tiene variables no inicializadas o punteros colgantes. Cuando una asignacion cambia
el valor almacenado en una celda de referencia, la asignacion debe ser coherente con el tipo
de la celda de referencia: una celda de referencia de tipo entero siempre contendra un
entero, una celda de referencia de tipo lista contendra siempre (o hara referencia a) una
lista, y as.
ML tiene operaciones para crear celdas de referencia, para acceder a su contenido y
para cambiar su contenido: ref, ! y :=, que se comportan como sigue:

ref v crea una celda de referencia que contiene el valor v


! r devuelve el valor contenido en la celda r
r: = v ubica el valor v en la celda de referencia r

3.3. Ejercicios
3.1. Las variables estan compuestas de... (elija la opcion mas precisa)
a) un nombre y un valor.
b) un l-valor y un r-valor.
c) un identificador y un valor.
d) un identificador que esta ligado a un valor que esta ligado a una direccion en
memoria.

17
e) un identificador que esta ligado a una direccion en memoria que contiene un
valor.
f) una direccion de memoria, un valor y un identificador en el texto del programa.

3.2. El identificador de una variable es...

a) la direccion de memoria donde se encuentra un valor que se usa en un programa.


b) el string que se usa en el texto de un programa para referirse a una variable.
c) el tipo del valor de la variable.

3.3. Los huecos (regiones del programa en los que una variable no se puede acceder) en
el alcance de una variable (identificador de variable) se dan porque...

a) el identificador de la variable se usa para ligarse a otra variable.


b) el lifetime de la variable no llega hasta ah.
c) tenemos un programa no estructurado.

18
Captulo 4

Estructura en bloques

4.1. C
odigo estructurado vs. c
odigo spaghetti
Mitchell 8.1
En lenguajes como ensamblador o Fortran es muy facil escribir programas que resulten
incomprensibles porque al que lee le cuesta entender la estructura de control del programa.
A este tipo de codigo se le llama codigo spaghetti, y aqu mostramos unos ejemplos:
1 10 IF ( X . GT . 0.000001) GO TO 20
X = -X
3 11 Y = X * X - SIN ( Y ) /( X +1)
IF ( X . LT . 0.000001) GO TO 50
5 20 IF ( X * Y . LT . 0.00001) GO TO 30
X = X -Y - Y
7 30 X = X + Y
...
9 50 CONTINUE
X=A
11 Y = B-A + C*C
GO TO 11
13 ...
En este ejemplo de Fortran de Mitchell (8:204) se ven algunos efectos del codigo spag-
hetti.
1 10 i = 0
20 i = i + 1
3 30 PRINT i ; " squared = " ; i * i
40 IF i >= 10 THEN GOTO 60
5 50 GOTO 20
60 PRINT " Program Completed . "
7 70 END

19
Este fragmento de codigo spaghetti podra traducirse a un lenguaje con bloques de la
siguiente forma:
1 10 FOR i = 1 TO 10
20 PRINT i ; " squared = " ; i * i
3 30 NEXT i
40 PRINT " Program Completed . "
5 50 END
Para evitar este problema, la mayora de los lenguajes de programacion modernos pro-
porcionan alguna forma de bloque. Un bloque es una region del texto del programa con
inicio y fin explcitos e inambiguos. Esta region del texto permite organizar de forma
explcita la logica del programa. Pero ademas, posibilita sucesivas abstracciones sobre el
flujo de ejecucion.
En primer lugar, los bloques nos permiten hacer declaraciones de variables locales. Por
ejemplo:
1 { int x = 2;
{ int y = 3;
3 x = y +2;
}
5 }
En esta seccion del codigo hay dos bloques. Cada bloque comienza con una llave izquier-
da, {, y termina con una llave derecha, }1 . El bloque exterior comienza con la primera llave
izquierda y termina con la u ltima llave derecha. El bloque interno se anida en el interior
del bloque exterior, comienza con la segunda llave izquierda y termina con la primera llave
derecha.
La variable x se declara en el bloque exterior y la variable y se declara en el bloque
interior. Una variable declarada dentro de un bloque se dice que es una variable local para
ese bloque. Las variables que no estan declaradas en un bloque, sino en alg un otro bloque
que lo contiene, se dice que es una variable global para el bloque mas interno. En este
ejemplo, x es local en el bloque exterior, y es local para el bloque interior, y x es global
para el bloque interior.
Gracias a la estructura de bloques se pueden implementar mecanismos de gestion de la
memoria que permiten, por ejemplo, el llamado a funciones de forma recursiva.
Las versiones de Fortran de los a nos 1960 y 1970 no eran estructuradas por bloques.
En Fortran historico, todas las variables, incluyendo todos los parametros de cada proce-
dimiento (llamado subrutina en Fortran) tenan una ubicacion fija en memoria. Esto haca
imposible llamar a un procedimiento de forma recursiva, ya fuera directa o indirectamente.
1
Los bloques se pueden delimitar con cualquier smbolo arbitrario, de hecho, los lenguajes de progra-
maci
on modernos ya no usan llaves sino espacios en blanco, que resultan mas naturales para el humano.
Un buen ejercicio es pensar la gram
atica que describira la sintaxis de delimitadores de bloque de Python,
como se pide en el ejercicio 2.18.

20
Si el procedimiento Fortran P llama Q, Q llama R, R y luego intenta llamar a P, la segunda
llamada a P no se puede realizar. Si P se llama por segunda vez en esta cadena de llamadas,
la segunda llamada escribira sobre los parametros y la direccion de la primera llamada,
por lo tanto, no se podra volver correctamente a la primera llamada.

4.2. Estructura de bloque


Mitchell 7.1
Los lenguajes con estructrua de bloque se caracterizan por las siguientes propiedades:

Las nuevas variables se pueden declarar en varios puntos de un programa.

Cada declaracion es visible dentro de una determinada region de texto del programa,
llamada bloque. Los bloques pueden ser anidados, pero no pueden superponerse par-
cialmente. En otras palabras, si dos bloques contienen expresiones o declaraciones en
comun, entonces un bloque debe estar enteramente contenida dentro del otro.

Pregunta 1:
Den un ejemplo de bloques superpuestos parcialmente, y argumenten que pro-
blemas podemos encontrar si permitimos este tipo de estructuras en nues-
tros programas.

Cuando un programa inicia la ejecucion de las instrucciones contenidas en un bloque


en tiempo de ejecucion, se asigna memoria a las variables declaradas en ese bloque.

Cuando un programa sale de un bloque, parte o toda la memoria asignada a las


variables declaradas en ese bloque se libera.

Un identificador de variable que no esta declarado en el bloque actual se considera


global a ese bloque, y su referencia es a la entidad con el mismo identificador nombre
que se encuentra en el bloque mas cercano que contiene al bloque actual.

Aunque la mayora de los lenguajes de programacion de proposito general modernos son


estructurados por bloques, muchos lenguajes importantes no explotan todas las capacidades
que permite la estructura por bloques. Por ejemplo, C y C ++ no permiten declaraciones
de funcion locales dentro de los bloques anidados, y por lo tanto no se ocupan de las
cuestiones de aplicacion asociados con el retorno de las funciones de los bloques anidados.
En este captulo, nos fijamos en como se manejan en memoria tres clases de variables:

variables locales que se almacenan en la pila de ejecucion, en el activation record aso-


ciado al bloque.

par
ametros de funci
on que tambien se almacenan en el activation record asociada con
el bloque.

21
Figura 4.1: Modelo simplificado de la computadora que usamos para describir la semantica
operacional.

variables globales que se declaran en alg un bloque que contiene al bloque actual y por
lo tanto hay que acceder a ellos desde un activation record que se coloco en la pila
de ejecucion antes del bloque actual.

La mayora de complicaciones y diferencias entre lenguajes estan relacionadas con el


acceso a las variables globales, a consecuencia de la estructura de la pila: para dejar mas a
mano las variables locales, una variable global puede quedar enterrado en la pila bajo un
n
umero arbitrario de activation records.

4.3. Activation records


Para describir la semantica de los lenguajes de programacion usamos una semantica
operacional, es decir, describimos los efectos de las diferentes expresiones del lenguaje
sobre una maquina. Para eso usamos un modelo simplificado de la computadora, el que se
ve en la figura 4.1.
Vamos a usar esta simplificacion para describir como funcionan en memoria los lenguajes
estructurados por bloques.
Vemos que este modelo de maquina separa la memoria en la que se guarda el codigo de la
memoria en la que se guardan los datos. Usamos dos variables para saber a que parte de la
memoria necesitamos acceder en cada momento de la ejecucion del programa: el contador
de programa y el puntero de entorno. El contador de programa es una direccion en
la parte de la memoria donde se guarda el codigo, en concreto, la direccion donde se

22
encuentra la instruccion de programa que se esta ejecuntando actualmente. Normalmente
se incrementa despues de ejecutar cada instruccion.
El puntero de entorno nos sirve para saber cuales son los valores que se asignan a las
variables que se estan usando en una parte determinada del codigo. En los lenguajes no
estructurados por bloques, la memoria de datos es no estructurada, todo el espacio es heap,
y por lo tanto los valores que se asignan a las variables son visibles desde cualquier parte
del codigo. En cambio, en los lenguajes estructurados por bloques, se guardan en el heap
algunos datos para los que necesitamos persistencia (por ejemplo, variables globales), en
los registros guardamos algunos datos para los que queremos rapido acceso, y en la pila de
ejecucion o stack se guardan los datos estructurados en forma de pila, lo cual hace posible
que se instancien variables locales y argumentos distintos para cada llamada de funcion.
Esto posibilita que las funciones puedan ser mas abstractas y que se puedan hacer llamadas
recursivas.
Pregunta 2:
Por que no se pueden hacer llamadas recursivas sin bloques ni activation re-
cords? Explique usando Fortran como ejemplo.
El stack o pila de ejecucion funciona de la siguiente forma: cuando el programa entra en
un nuevo bloque, se agrega a la pila una estructura de datos que se llama activation record
o marco de pila (stack frame), que contiene el espacio para las variables locales declaradas
en el bloque, normalmente, por la parte de arriba de la pila. Entonces, el puntero de
entorno apunta al nuevo activation record. Cuando el programa sale del bloque, se retira
el activation record de la pila y el puntero de entorno se restablece a su ubicacion anterior,
es decir, al puntero de entorno correspondiente a la funcion que llamaba a la funcion que
ha sido desapilada. El activation record que se apila mas recientemente es el primero en
ser desapilado, a esto se le llama disciplina de pila.
En la figura 4.2 podemos ver la informacion que hay en un activation record de arqui-
tectura x86. Vemos que en esta arquitectura hay, ademas de espacio para variables locales,
tambien espacio para los argumentos que puede recibir una funcion, que funcionan como
variables locales con respecto a la funcion pero que son escritos por la funcion que la llama.
Tambien encontramos espacio para guardar resultados intermedios, en el caso de que sea
necesario. Podemos observar que hay dos direcciones de memoria donde se guardan datos
importantes: una, el control link, contiene el que sera el puntero de entorno cuando se
desapile el activation record actual, es decir, el puntero de entorno del activation record
correspondiente a la funcion que llamaba a la funcion del activation record actual. La otra
direccion de memoria distinguida es la llamada direccion de retorno, que es donde se va a
guardar el resultado de la ejecucion de la funcion, si es que lo hay.
Los activation records pueden tener tama nos variables, por lo tanto, las operaciones que
ponen y sacan activation records de la pila guardan tambien en cada activation record una
variable con la direccion de memoria que corresponde a la parte superior del registro de
activacion anterior. Esta variable o puntero (porque apunta a una direccion de memoria)
se llama control link, porque es el enlace que se sigue cuando se devuelve el control a la
instruccion en el bloque precedente.

23
Figura 4.2: Esquema de los contenidos de un activation record tpico de la arquitectura
x86.

Pregunta 3:
Aunque la mayora de lenguajes estructurados por bloques se ejecutan con una
pila, las funciones de alto orden pueden hacer que falle la disciplina de pila.
Por que?

Pregunta 4:
Se pueden hacer algunas optimizaciones sobre el funcionamiento de los activa-
tion records, cuales se le ocurren?

Pregunta 5:
Los programas estructurados por bloques y su traslado a activation records en
una parte de la memoria permiten las llamadas recursivas. Pero hacer muchas
llamadas recursivas puede crear un problema en la memoria. Como se llama
este problema y como lo solucionan los compiladores de algunos lenguajes?

Pregunta 6:
Por que pueden tener tama
nos variables los activation records?

El lenguaje de programacion C esta dise nado para ser facil de compilar y ejecutar,
evitando varias de las propiedades de alcance y de manejo de memoria en general que
se describen en este captulo. Sin embargo, algunas implementaciones comerciales de C y
C++ permiten tomar funciones como parametros de otras funciones y devolver funciones
como resultado de otras funciones, preservando el alcance estatico con clausuras.

24
En la siguiente seccion vamos a ver como funcionan los activation records en ejemplos
de ejecucion. Para una explicacion alternativa, pueden consultar la seccion correspondiente
de la wikipedia sobre la estructura de pila de llamadas.

4.3.1. Detalle de ejecuci


on de un activation record
Mitchell 7.2
Describiremos el comportamiento de los bloques in line primero, ya que estos son mas
simples que los bloques asociados a las llamadas de funcion. Un bloque in line es un bloque
que no es el cuerpo de una funcion o procedimiento.
Cuando un programa en ejecucion entra en un bloque in line, se asigna espacio en
memoria para las variables que se declaran en el bloque, un conjunto de posiciones de
memoria en la pila, el activation record.
Veamos como funciona paso a paso, con el siguiente ejemplo de codigo.
1 { int x =0;
int y = x +1;
3 { int z =( x + y ) * (x - y ) ;
};
5 }
Si este codigo es parte de un programa mas grande, la pila puede contener espacio para
otras variables antes de ejecutar este bloque.
Este codigo estara escrito en codigo maquina, traducido por el compilador, y guardado
en la parte de la memoria en la que se guarda el programa. El contador de programa
recorrera cada una de sus instrucciones, y realizara las acciones que indiquen en memoria,
tal como sigue.
Cuando se introduce el bloque exterior, se pone en la parte superior de la pila un
activation record que contiene espacio para las variables x e y. A continuacion se ejecutan
las declaraciones que establecen valores de x e y, y los valores de x e y se almacenan en las
direcciones de memoria del activation record.
Cuando se introduce el bloque interior, se apila un nuevo activation record que con
direcciones en memoria para z. Despues de establecer el valor de z, el activation record
que contiene este valor se retira de la pila y el puntero de entorno deja de referirse a este
activation record y pasa a referirse al activation record que esta ahora en la cabeza de la
pila. Por ultimo, cuando se sale del bloque exterior, el activation record que con x e y se
retira de la pila y el puntero de entorno se refiere al activation record que este en la cabeza
de la pila o a ninguno si no hay mas activation records en la pila.
Podemos visualizar esta ejecucion usando una secuencia de graficos que describen la
secuencia de estados por los que pasa la pila, como se ve en la figura 4.3.
El numero de direcciones en memoria que va a necesitar un activation record en tiempo
de ejecucion depende del n umero de variables declaradas en el bloque y sus tipos. Debido a
que estas cantidades se conocen en tiempo de compilacion, el compilador puede determinar

25
Figura 4.3:

Figura 4.4:

26
el formato de cada activation record y almacenar esta informacion como parte del codigo
compilado.
En general, un activation record tambien puede contener espacio para resultados inter-
medios, como hemos visto en la figura 4.2. Estos son valores que no reciben un identificador
de variable explcito en el codigo, pero que se guardan temporalmente para facilitar alg
un
calculo.
Por ejemplo, el registro de activacion para este bloque,
1 { int z = ( x + y ) * (x - y ) ; }
puede tener espacio para asignar valor a la variable z pero tambien para los valores x+y
y x-y, porque los valores de estas subexpresiones pueden tener que ser evaluados y alma-
cenados en alg un lugar antes de multiplicarse. Si hacemos zoom en el tercer estado de la
pila de la figura 4.3, veremos lo que se muestra en la figura 4.4, con espacio para valores
temporales.
En las computadoras modernas, hay suficientes registros en memoria para guardar
estos resultados intermedios en los registros y no en la pila. No hablamos de ubicacion de
registros porque es un tema que compete a los compiladores y no al dise no de lenguajes de
programacion.

4.4. Ejercicios
Seleccione la(s) respuesta(s) correcta(s)
4.1. Los activation records representan...

a) unidades en la pila de ejecucion


b) stack frames
c) bloques del texto del programa
d) todos los anteriores
e) ninguno de los anteriores

4.2. Todo activation record necesariamente tiene...

a) control link
b) access link
c) variables locales
d) todas las anteriores
e) ninguna de las anteriores

4.3. Los activation records...

27
a) muestran el estado de una maquina abstracta en un momento de la ejecucion.
b) se corresponden con los bloques en el texto del programa.
c) se apilan en la pila de ejecucion.
d) todos los anteriores.
e) ninguno de los anteriores.

4.4. El siguiente codigo es codigo espagueti porque...


1 10 IF ( X . GT . 1) GO TO 30
20 X = -X
3 IF ( X . LT . 1) GO TO 60
30 IF ( X * Y . LT . 1) GO TO 40
5 X = X -Y - Y
40 X = X + Y
7 60 CONTINUE
X = A
9 Y = B-A
GO TO 20

a) tiene bloques.
b) las lneas estan numeradas.
c) no podemos armar un arbol sintactico del programa.
d) usa expresiones GOTO.

4.5. En el siguiente programa, la segunda ocurrencia de P es libre o ligada?


declare P in
2 proc { P X }
if X > 0 then { P X -1 } end
4 end

a) libre
b) ligada

Ejercicios pr
acticos
4.1. El siguiente es un ejemplo de spaghetti code. Reescrbalo de forma que NO use
saltos (GOTO), y en cambio use programacion estructurada.

28
10 i = 0
2 20 i = i + 1
30 PRINT i ; " squared = " ; i * i
4 40 IF i >= 10 THEN GOTO 60
50 GOTO 20
6 60 PRINT " Program Completed . "
70 END

4.2. Diagrame los estados de la pila de ejecucion en los diferentes momentos de la ejecucion
del siguiente programa, mostrando como se apilan y desapilan los diferentes activation
records a medida que se va ejecutando el programa. Puede representar los activation
records con la informacion de variables locales y control links.
1 { int x =2 { int y =3; x = y +2;}}

4.3. Diagrame como en el ejercicio anterior.


1 int sum ( int n ) {
if ( n ==0)
3 return n ;
else
5 return n + sum (n -1) ;
}
7 sum (4) ;

29
Captulo 5

Control de la ejecuci
on

5.1. Funciones y procedimientos


Mitchell 7.3

5.2. Pasaje de par


ametros
Mitchell 7.3.2
https://en.wikipedia.org/wiki/Evaluation_strategy
Los nombres de los parametros que se usan cuando se declara una funcion se llaman
parametros formales. Cuando se llama a una funcion, se usan los llamados parametros
reales, que son los valores efectivos de los parametros formales en una llamada concreta a
la funcion. Ilustraremos la distincion entre los parametros formales y los reales se ilustra
en el siguiente codigo:
1 proc p ( int x , int y ) {
if ( x > y ) then . . . else . . . ;
3 ...
x := y * 2 + 3;
5 ...
}
7 p (z , 4 * z +1) ;
Los identificadores x e y son parametros formales del procedimiento p. Los parametros
reales en la llamada a p son z y 4 z + 1.
La forma en que los parametros reales se eval uan y se pasan a la funcion depende del
lenguaje de programacion y el tipo de mecanismo de pasaje de parametros que utiliza. Los
distintos mecanismos de pasaje de parametros se diferencian por el momento en el que
se eval
ua el parametro real (evaluacion estricta, en el momento de pasar el parametro, o
perezosa, cuando se necesita) y por la ubicacion de memoria donde se almacena el valor

30
del parametro (la misma ubicacion del bloque que hace la llamada a funcion o una nueva
ubicacion especfica de la funcion).
La mayora de los lenguajes de programacion actuales eval uan los parametros reales de
forma estricta, no perezosa, pero hay algunas excepciones. (Una de las razones para que un
lenguaje o optimizacion de programa podra querer retrasar la evaluacion de un parametro
real es que la evaluacion podra ser costosa y el parametro real podra no ser utilizado en
algunas llamadas.) Entre los mecanismos que eval uan el parametro real antes de ejecutar
el cuerpo de la funcion, los mas comunes son el pasaje por referencia y el pasaje por
valor. Estos dos mecanismos se diferencian entre ellos por la ubicacion de memoria donde
se almacena el valor del parametro. El pasaje por referencia pasa el L-valor, es decir, la
ubicacion en memoria, del parametro real, mientras que el pasaje por valor pasa el R-valor,
es decir, el valor que hay en la ubicacion de memoria.
La diferencia entre el pasaje por valor y el pasaje por referencia es importante para el
programador por varias razones:

Efectos secundarios Las asignaciones de valor en el interior del cuerpo de la funcion


pueden tener diferentes efectos.

Aliasing el aliasing ocurre cuando dos identificadores de variable se refieren al mismo


objeto o ubicacion. Esto puede ocurrir cuando dos parametros se pasan por referencia
o un parametro pasado por referencia tiene la misma ubicacion que la variable global
del procedimiento.

Eficiencia Pasar por valor puede ser ineficaz para grandes estructuras de datos, si hay que
copiar ese valor. Pasar por referencia puede ser menos eficiente que pasar por valor
pequenas estructuras que se adapten bien en la pila, porque cuando los parametros
se pasan por referencia, debemos resolver la referencia de un puntero para obtener
su valor.

Hay dos formas de describir la semantica del pasaje de parametros: con diagramas de
la memoria y la pila que muestren si la pila contiene una copia del parametro real o una
referencia. Otra forma es traducir el codigo a un lenguaje que distinga entre R-valores y
L-valores, que es lo que vamos a hacer aqu.

5.2.1. Sem
antica de pasaje por valor (call-by-value)
El pasaje por valor es la estrategia de evaluacion mas comun en los lenguajes de pro-
gramacion. Es una forma estricta de pasaje de parametros, es decir, que el argumento se
eval
ua, y el valor que se obtiene (su R-valor) se liga a una variable local de la funcion, en
general copiando el valor a una nueva ubicacion de memoria en el activation record de la
llamada a funcion. El valor que se encuentra en el activation record que llama a la funcion
no es modificado en ning un momento.
Por ejemplo, veamos esta definicion de funcion y su llamada:

31
1 f ( x ) = { x : = x + 1; volver x };
... . f ( y ) ... ;
Si el parametro se pasa por valor e y es una variable entera, entonces este codigo tiene
el mismo significado que el siguiente codigo ML:
fun f ( z : int ) = let x = ref z in x :=! x +1;! x end ;
2 ... f (! y ) ... ;
Como se puede ver en el tipo, el valor pasado a la funcion f es un n umero entero. El
entero es el R-valor del parametro real y, como lo indica la expresion !y en la llamada. En
el cuerpo de f , se asigna y se inicializa una nueva ubicacion para el R-valor de y.
Si el valor de y es 0 antes de la llamada, entonces el valor de f (!y) es 1 porque la
funcion f incrementa el parametro y devuelve su valor. Sin embargo, el valor de y despues
de la llamada es a un 0, porque la asignacion dentro del cuerpo de f solamente cambia el
contenido de una ubicacion temporal.

5.2.2. Sem
antica de pasaje por referencia call-by-reference
En pasaje por referencia se liga el L-valor del parametro real al parametro formal. En
este caso se tiene un unico valor referenciado (o apuntado) desde dos puntos diferentes,
el programa principal y la funcion a la que se le pasa el argumento, por lo que cualquier
accion sobre el parametro se realiza sobre la misma posicion de memoria (aliasing).
Esto significa que la funcion que es llamada puede modificar la variable con efecto
en el bloque que llama a la funcion. De esta manera, el pasaje por referencia se puede
usar como un canal de comunicacion entre la funcion que llama y la que es llamada, pero
a la vez resulta mas difcil seguir el rastro de los efectos de una llamada a funcion, y se
pueden introducir errores sutiles. Por esta razon, aunque hay muchos lenguajes que ofrecen
pasaje por referencia, pocos lo usan como pasaje por defecto, por ejemplo, Perl. Algunos
lenguajes, como C++, PHP, Visual Basic .NET o C# tienen pasaje por valor por defecto
pero ofrecen una sintaxis especfica para pasar parametros por referencia.
Veamos el detalle de la semantica de pasaje por referencia.
Considere la misma definicion de la funcion y de llamadas utilizada en la explicacion
de pasaje por valor:
f ( x ) = { x : = x + 1; volver x };
2 ... . f ( y ) ... ;
Si el parametro se pasa por referencia e y es una variable entera, entonces este codigo
tiene el mismo significado que el siguiente codigo de ML:
fun f ( x : int ref ) = ( x := ! x +1; ! x ) ;
2 ... f ( y )

32
Como se puede ver en el tipo, el valor pasado a la funcion f es una referencia, un
L-valor. Si el valor de y es 0 antes de la llamada, entonces el valor de f (!y) es 1 porque
la funcion f incrementa el parametro y devuelve su valor. Sin embargo, a diferencia de la
situacion para el pasaje por valor, el valor de y es 1 tambien despues de la llamada, debido
a que la asignacion dentro del cuerpo de f tambien cambia el valor del parametro real.
Veamos un ejemplo, escrito en una notacion asimilable a Algol, que combina pasaje por
referencia y pasaje por valor:
fun f ( pass - by - ref x : int , pass - by - value y : int )
2 begin
x := 2;
4 y := 1;
if x = 1 then return 1 else return 2;
6 end ;
var z : int ;
8 z := 0;
print f (z , z ) ;
Si traducimos este ejemplo en pseudo-Algol a ML nos da:
1 fun f ( x : int ref , y : int ) =
let val yy = ref y in
3 x := 2;
yy := 1;
5 if (! x = 1) then 1 else 2
end ;
7 val z = ref 0;
f (z ,! z ) ;
Este codigo, que trata a los R-valores y L-valores explcitamente, muestra que para el
pasaje por referencia se pasa un L-valor, la referencia z. Para pasaje por valor, se pasa un
R-valor, el contenido !z de z, y se le asigna una nueva ubicacion temporal.
Si se pasa y por valor tal como se ve, a z se le asigna el valor 2. En cambio, si y se
hubiera pasado por referencia, entonces x e y habran sido alias de la misma variable y a
z se le habra asignado el valor 1.
En este otro ejemplo tenemos una funcion que comprueba si sus dos parametros son
alias:
function (y , z ) {
2 y := 0;
z :=0;
4 y := 1;
if z =1 then y :=0; return 1 else y :=0; return 0
6 }

33
Si y y z son alias, entonces asignar 1 a y asignara 1 a z tambien, y la funcion devolvera 1.
De lo contrario, la funcion devolvera 0. Por lo tanto, una llamada f (x, x) tendra diferentes
resultados si los parametros se pasan por valor o por referencia.

5.2.3. Sutilezas entre pasaje por valor y pasaje por referencia


En lenguajes puramente funcionales tpicamente no hay diferencia semantica entre pa-
saje por valor y pasaje por referencia, porque sus estructuras de datos son inmutables,
por lo que es imposible que una funcion pueda modificar sus argumentos. Por esta razon
normalmente se describen como de pasaje por valor, a pesar de que las implementaciones
utilizan normalmente pasaje por referencia internamente porque es mas eficiente.
En algunos casos de pasaje por valor el valor que se pasa no es el habitual de la variable,
sino una referencia a una ubicacion de memoria. El efecto es que lo que sintacticamente
parece llamada por valor puede terminar teniendo los efectos de un pasaje por referencia.
Lenguajes como C y ML utilizan esta tecnica. No es una estrategia de evaluacion indepen-
diente, ya que el lenguaje sigue teniendo pasaje por valor pero a veces se llama pasaje por
direccion de memoria. En un lenguaje con poca seguridad como C puede causar errores de
seguridad de la memoria como desreferencia de puntero nulo, y tambien puede ser confuso.
En cambio, en ML las referencias tienen seguridad de tipo y de memoria.
Aqu vemos un ejemplo de pasaje por direccion que simula pasaje por referencia en C:
void Modify ( int p , int * q , int * o )
2 {
p = 27; // pasado por valor - solo se modifica el
parametro local
4 * q = 27; // pasado por valor o referencia , verificar en
punto de llamada para comprobar cual
* o = 27; // pasado por valor o referencia , verificar en
punto de llamada para comprobar cual
6 }
int main ()
8 {
int a = 1;
10 int b = 1;
int x = 1;
12 int * c = & x ;
Modify (a , &b , c ) ; // a se pasa por valor , b se pasa por
referencia creando un puntero ,
14 // c es un puntero pasado por valor
// b y x han sido cambiados
16 return (0) ;
}

34
Hay varias razones por las que se puede pasar una referencia. Una razon puede ser
que el lenguaje no permita el pasaje por valor de estructuras de datos complejas, y se
pase una referencia para preservar esa estructura. Tambien se usa de forma sistematica en
lenguajes con orientacion a objetos, lo que se suele llamar call-by-sharing, call-by-object o
call-by-object-sharing.
En call-by-sharing, el pasaje de parametros es por valor, pero se pasa una referencia a
un objeto. Por lo tanto, se hace una copia local del argumento que se pasa, pero esa copia
es una referencia a un objeto que es visible tanto desde la funcion llamada como desde la
funcion que llama.
La semantica de call-by-sharing difiere del pasaje por referencia en que las asignaciones
a los argumentos dentro de la funcion no son visibles a la funcion que llama, por lo que, por
ejemplo, si se pasa una variable, no es posible simular una asignacion en esa variable en el
alcance de la funcion que llama. Por lo tanto, no se pueden hacer cambios a la referencia
que se ha pasado por valor.
Sin embargo, a traves de esa referencia de la que la funcion llamada ha hecho una
copia, ambas funciones, la llamada y la que llama, tienen acceso al mismo objeto, no se
hace ninguna copia. Si ese objeto es mutable, los cambios que se hagan al objeto dentro de
la funcion llamada son visibles para la funcion que llama, a diferencia del pasaje por valor.
Este es el comportamiento de Java, Python, Ruby, JavaScript o OCaml. Sin embargo,
el termino call-by-sharing no se usa mucho, se usan diferentes terminos en los diferentes
lenguajes y documentaciones. Por ejemplo, en la comunidad Java dicen que Java tiene
pasaje por valor, mientras que en la comunidad Ruby dicen que Ruby tiene pasaje por
referencia, a pesar de que los dos lenguajes tienen la misma semantica. El call-by-sharing
implica que los valores que se pasan por parametro se basan en objetos en lugar de tipos
primitivos, es decir que todos los valores estan empaquetados.
Resuelva el ejercicio 5.11 para profundizar sobre este tipo de pasaje de parametros.

5.2.4. Sem
antica de pasaje por valor-resultado
Es un tipo de pasaje de parametros con evaluacion estricta (no perezosa) poco usado
en los lenguajes de programacion actuales. Se basa en que dentro de la funcion se trabaja
como si los argumentos hubieran sido pasados por valor, pero al acabar la funcion los
valores que tengan los argumentos seran copiados a la ubicacion de memoria en la que se
ubicaba el valor copiado inicialmente.
Este tipo de pasaje de parametros puede ser simulado en cualquier lenguaje que permita
el paso de valores por referencia de la siguiente forma:
1 void EjemploValorRes ( int a1 , int a2 , int a3 ) {
int aux1 = a1 , aux2 = a2 , aux3 = a3 ;
3 // codigo trabajando con aux1 , aux2 y aux3
a1 = aux1 ; a2 = aux2 ; a3 = aux3 ; // Dependiendo del
compilador la copia se realiza en un sentido o en el otro
5 }

35
Tal y como indica el ejemplo de simulacion de valor-resultado, el orden de copia depende
del compilador, lo que implica que la misma funcion pueda dar resultados diferentes seg un
el compilador usado.

Pregunta 1:
Que ventajas y que desventajas tiene pasaje por valor-resultado?

Pregunta 2:
Haga un programa como en la seccion anterior, que tenga diferentes resultados
si los parametros se pasan por valor, por referencia o por valor-resultado.

5.2.5. Pasaje de par


ametros no estricto (perezoso)
En la evaluacion perezosa, los argumentos de una funcion no se eval
uan a menos que
se utilicen efectivamente en la evaluacion del cuerpo de la funcion.

Pregunta 3:
La evaluacion de las alternativas de expresiones condicionales debe conside-
rarse estricta o perezosa?

Pregunta 4:
Se le ocurre alguna forma de optimizacion de la evaluacion de expresiones
complejas, usando evaluacion perezosa y las propiedades del absorbente de un
operador?

Pasaje por nombre call-by-name


En el pasaje call-by-name, los argumentos de una funcion no se eval uan antes de la
llamada a la funcion, sino que se sustituyen sus nombres directamente en el cuerpo de la
funcion y luego se dejan para ser evaluados cada vez que aparecen en la funcion. Si un
argumento no se utiliza en el cuerpo de la funcion, el argumento no se eval
ua; si se utiliza
varias veces, se re-eval
ua cada vez que aparece.
En algunas ocasiones, call-by-name tiene ventajas sobre pasaje por valor, por ejemplo,
si el argumento no siempre se usa, porque nos ahorramos la evaluacion del argumento. Si el
argumento es un calculo no termina, la ventaja es enorme. Sin embargo, cuando se utiliza
el argumento en la funcion, el pasaje por nombre es a menudo mas lento.
Este tipo de pasaje se uso en ALGOL 60. Scala tiene por defecto pasaje por valor, pero
tambien pueden hacer pasaje por nombre. Eiffel provee agentes, operaciones que seran
evaluadas cuando sea necesario.

36
Pasaje por necesidad call-by-need
El pasaje por necesidad call-by-need es una version memoizada de pasaje por nombre
donde, si se eval
ua el argumento de la funcion, ese valor se almacena para usos posteriores.
En un entorno de puro(sin efectos secundarios), esto produce los mismos resultados que
el pasaje por nombre; cuando el argumento de la funcion se utiliza dos o mas veces, el
pasaje por necesidad es casi siempre mas rapido.
Haskell es el lenguaje mas conocido que utiliza call-by-need. R tambien utiliza una
forma de pasaje por necesidad. Variantes de .NET pueden simular llamada por necesidad
utilizando el tipo LazyT.

Pregunta 5:
Haga un programa como en la seccion anterior, que tenga diferentes resultados
si los parametros se pasan por nombre, por necesidad o por alg
un otro metodo.

5.2.6. Ejercicios
5.1. Si el Browse del siguiente programa muestra 4, que tipo de pasaje de parametros
tiene?
1 declare A =2 B =4 C =1
proc { P X Y Z}
3 C =5
X=Z
5 C =4
Y=Z+A
7 end
in { P A B A+C}
9 { Browse B}

a) por valor
b) por referencia
c) por valor-resultado
d) por nombre
e) por por necesidad

5.2. Si el Browse del siguiente programa muestra 5, que tipo de pasaje de parametros
tiene?
1 declare A =2 B =4 C =1
proc { P X Y Z }
3 C =5
X=Z

37
5 C =4
Y=Z+A
7 end
in { P A B A + C }
9 { Browse B }

a) por valor
b) por referencia
c) por valor-resultado
d) por nombre
e) por por necesidad

5.3. La ventaja del pasaje de parametros por referencia es que...

a) reduce aliasing.
b) ocupa menos lugar en la memoria.
c) no tiene efectos fuera de la funcion llamada.
d) permite actuar en la funcion que llama, si el programador lo necesita.

5.4. La ventaja del pasaje de parametros por valor es que...

a) reduce aliasing.
b) ocupa menos lugar en la memoria.
c) no tiene efectos fuera de la funcion llamada.
d) permite actuar en la funcion que llama, si el programador lo necesita.

5.5. La ventaja del pasaje de parametros por valor-resultado es que...

a) reduce aliasing.
b) ocupa menos lugar en la memoria.
c) no tiene efectos fuera de la funcion llamada.
d) permite actuar en la funcion que llama, si el programador lo necesita.

5.6. Los metodos de pasaje por parametros que siguen la filosofa de evaluacion perezosa
(lazy evaluation) son...

a) pasaje por referencia.


b) pasaje por valor.
c) pasaje por valor-resultado.
d) pasaje por nombre.

38
e) pasaje por necesidad.

5.7. En el siguiente programa,

begin
integer n;
procedure p(k: integer);
begin
k := k+2;
print(n);
n := n+(2*k);
end;
n := 4;
p(n);
print(n);
end;

Que dos valores se imprimen si k se pasa...

a) por valor?
b) por valor-resultado?
c) por referencia?

5.8. [10 pt.] En el siguiente programa:

begin
integer a, b, c;
procedure p(x: integer, y: integer, z: integer);
begin
c := 5;
x := z;
c := 4;
y := z+a
end;
a := 2;
b := 4;
c := 1;
p(a, b, a+c);
print(b);
end;

Que dos valores se imprimen si k se pasa...

a) por valor?

39
b) por valor-resultado?
c) por referencia?

5.9. En los siguientes fragmentos de programa, introduzcan los elementos necesarios para
determinar si C y Perl funcionan por call-by-value o call-by-reference:
Fragmento en C:
1 int main ( void ) {
int x =50 , y =70;
3 ...
printf ( " x= %d y= %d " , x , y ) ;
5 return 0;
}
7

void interchange ( int x1 , int y1 ) {


9 int z1 ;
...
11 ...
...
13 printf ( " x1= %d y1= %d " , x1 , y1 ) ;
}

Fragmento en Perl:
$x =50;
2 $y =70;
& interchange ( $x , $y ) ;
4 print " x : $x y : $y \ n " ;

6 sub interchange {
( $x1 , $y1 ) = @_ ;
8 ...
...
10 ...
print " x1 : $x1 y1 : $y \ n " ;
12 }

5.10. Que diferencia hay en Scala entre pasar una variable por valor, pero lazy, a pasar
a una variable por nombre? Cree un programa que muestre estas diferencias.

5.11. Describa que sucede en el siguiente programa en Java, teniendo en cuenta el meca-
nismo de pasaje de parametros que utiliza el lenguaje.
public class Point {
2 public int x ;

40
public int y ;
4

public Point ( int x , int y ) {


6 this . x = x ;
this . y = y ;
8 }

10 public static void tricky1 ( Point arg1 , Point arg2 )


{
12 arg1 . x = 100;
arg1 . y = 100;
14 Point temp = arg1 ;
arg1 = arg2 ;
16 arg2 = temp ;
}
18

public static void tricky2 ( Point arg1 , Point arg2 )


20 {
arg1 = null ;
22 arg2 = null ;
}
24

public static void main ( String [] args )


26 {
Point pnt1 = new Point (0 ,0) ;
28 Point pnt2 = new Point (0 ,0) ;
System . out . println ( " pnt1 X : " + pnt1 . x + " pnt1 Y : "
+ pnt1 . y ) ;
30 System . out . println ( " pnt2 X : " + pnt2 . x + " pnt2 Y : "
+ pnt2 . y ) ;
System . out . println ( " triki1 \ n " ) ;
32 tricky1 ( pnt1 , pnt2 ) ;
System . out . println ( " pnt1 X : " + pnt1 . x + " pnt1 Y : " +
pnt1 . y ) ;
34 System . out . println ( " pnt2 X : " + pnt2 . x + " pnt2 Y : "
+ pnt2 . y ) ;
System . out . println ( " \ ntriki2 " ) ;
36 tricky2 ( pnt1 , pnt2 ) ;
System . out . println ( " pnt1 X : " + pnt1 . x + " pnt1 Y : " +
pnt1 . y ) ;
38 System . out . println ( " pnt2 X : " + pnt2 . x + " pnt2 Y : "
+ pnt2 . y ) ;
}

41
40 }

5.3. Alcance est


atico vs. alcance din
amico
Mitchell 7.3.3
Si un identificador x aparece en el cuerpo de una funcion, pero x no se declara dentro
de la funcion, entonces el valor de x depende de alguna declaracion fuera de la funcion.
En esta situacion, la ubicacion de x esta fuera del registro de activacion para la funcion y
es una variable global a esa funcion. Debido a que x ha sido declarada en otro bloque, el
acceso a una x libre o global consiste en encontrar el registro de activacion pertinente en
la pila.
Hay dos polticas principales para buscar la declaracion adecuada de un identificador
global:

alcance estatico Un identificador global se refiere al identificador con ese nombre que se
declara en el bloque contenedor mas cercano del texto del programa.

alcance dinamico Un identificador global se refiere al identificador asociado con el regis-


tro de activacion mas reciente.

Estas definiciones pueden ser difciles de entender, as que aseg


urese de entender con
precision los ejemplos. Una diferencia importante entre el alcance estatico y el dinamico es
que la busqueda de una declaracion usando la poltica de alcance estatico utiliza la relacion
estatica e inmutable entre bloques en el texto del programa. Por el contrario, el alcance
dinamico utiliza la secuencia efectiva de las llamadas que se ejecutan en la ejecucion del
programa, que es dinamica y puede cambiar.
Aunque la mayora de los lenguajes actuales de programacion de proposito general uti-
lizan alcance estatico para las declaraciones de variables y funciones, el alcance dinamico
es un concepto importante que se utiliza en lenguajes de proposito especfico y en cons-
trucciones especializadas como las excepciones. Algunos lenguajes con alcance dinamico
son los dialectos antiguos de Lisp, los lenguajes de marcado Tex/LaTex, las excepciones y
las macros.

Pregunta 6:
En que contextos le parece que sera u
til tener alcance dinamico?

5.3.1. Ejercicios
5.1. La clausura de una funcion es necesaria para...

a) definir funciones en un alcance global


b) mantener la referencia a las variables locales de la funcion si tenemos alcance
dinamico

42
c) mantener la referencia a las variables locales de la funcion si tenemos alcance
estatico
d) mantener la referencia a las variables libres de la funcion si tenemos alcance
dinamico
e) mantener la referencia a las variables libres de la funcion si tenemos alcance
estatico
5.2. Si el resultado del siguiente programa es 19, que tipo de alcance tiene el lenguaje de
programacion en el que esta escrito?
val x = 4;
2 fun f ( y ) = x * y ;
fun g ( x ) = let
4 f (3) + x ;
g (7) ;

a) estatico
b) dinamico

5.3. Diagrame los estados de la pila de ejecucion en los diferentes momentos de la ejecucion
del siguiente programa, mostrando como se apilan y desapilan los diferentes activation
records a medida que se va ejecutando el programa. Puede representar los activation
records con la informacion de variables locales y control links. A
nada en el activation
record la informacion necesaria para poder recuperar la informacion de la funcion,
con alcance estatico.
1 int sum ( int n ) {
if ( n ==0)
3 return n ;
else
5 return n + sum (n -1) ;
}
7 sum (4) ;

5.4. Diagrame como en el ejercicio anterior.


1 bool isPalindrome ( char * s , int len )
{
3 if ( len < 2)
return TRUE ;
5 else
return s [0] == s [ len -1] && isPalindrome (& s [1] , len -2) ;
7 }
isPalindrome ( ada )

43
5.5. Diagrame como en el ejercicio anterior.
fact ( n ) = if n <=1 then 1 else n * fact (n -1) ; fact (4) ;

5.6. Diagrame como en el ejercicio anterior, pero ahora con alcance dinamico.
1 var x =1;
function g ( z ) { return x + z ;}
3 function f ( y ) {
var x = y +1;
5 return g ( y * x ) ;
}
7 f (3)

5.7. Explique el funcionamiento del siguiente programa en pseudocodigo con alcance


estatico y con alcance dinamico. Ay
udese de diagramas de active records si le re-
sulta util.
1 local D R Div in
D =5
3 fun Div ( X )
X div D
5 end
R = Div (10)
7 Print ( R )
local D =0 R2 in
9 R2 = Div (10)
Print ( R2 )
11 end
end

5.8. Diagrame la pila de ejecucion del siguiente programa en Bash, incluyendo la estruc-
tura de control links y access links, asumiendo que el lenguaje de programacion tiene
alcance estatico.
x =1
2 function g () { echo $x ; x =2 ; }
function f () { local x =3 ; g ; }
4 f # does this print 1 , or 3?
echo $x

En realidad este programa esta escrito en Bash, que tiene alcance dinamico. Justifique
por que puede ser que Bash se haya dise nado con lenguaje dinamico.
Que imprimira este programa, ahora que sabemos que tiene alcance dinamico?

44
5.9. Reescriba el siguiente programa para que sea un programa en C estandar (sin usar
gcc con -fnested-functions) con la misma semantica.
1 void f ( void )
{
3 // Define a function called g
void g ( void )
5 {
}
7 // Call g
g () ;
9 }

5.10. Reescriba el siguiente programa en Python para que sea valido en Perl en modo
estricto, poniendo atencion al lugar de declaracion de la variable, para que su alcance
sea exactamente el mismo en ambos programas.
1 if c :
a = foo
3 else :
a =

5.11. Determine si los siguientes lenguajes tienen alcance estatico o alcance dinamico:
Python, Haskell, Java, Scala, C, Javascript y Ruby. Para cada uno de estos len-
guajes, cree un programa (simple) que lo demuestre.

5.4. Recursi
on a la cola
Mitchell 7.3.4
En este apartado nos fijamos en una optimizacion del compilador que se llama elimi-
nacion de la recursion a la cola. Para las funciones recursivas a la cola, que describimos
a continuacion, se puede reutilizar un activation record para una llamada recursiva a la
funcion. Esto reduce la cantidad de espacio de la pila que usa una funcion recursiva, y
evita llegar a problemas por lmites de hardware como el llamado stack overflow, en el que
la ejecucion de un programa requiere mas espacio del que hay disponible en la pila.
El principal concepto de lenguajes de programacion que necesitamos entender es el
concepto de la llamada a la cola. Supongamos que la funcion f llama a la funcion g. f y g
podran ser diferentes funciones o la misma, haciendo una llamada recursiva. Una llamada
a f en el cuerpo de g es una llamada de la cola si g devuelve el resultado de la llamada f
sin ningun calculo adicional. Por ejemplo, en la funcion
fun g ( x ) = if x =0 then f ( x ) else f ( x ) * 2

45
Figura 5.1: Estado de la pila despues de tres llamadas a factcola sin optimizacion del
compilador.

la primera llamada a f en el cuerpo de g es una llamada de la cola, ya que el valor de


retorno de g es exactamente el valor de retorno de la llamada a f. La segunda llamada a
f en el cuerpo de g no es una llamada de la cola porque g realiza un calculo que involucra
el valor de retorno de f antes de que g retorne.
Una funcion f es recursiva de cola si todas las llamadas recursivas en el cuerpo de f
son llamadas a la cola a f.
Veamos como ejemplo la funcion recursiva a la cola que calcula el factorial:
1 fun factcola (n , a ) = if n <= 1 then a else factcola (n -1 , n *
a);
Para cualquier entero positivo n, factcola (n, a) devuelve n!. Podemos ver que
factcola es una funcion recursiva de cola porque la u
nica llamada recursiva en el cuerpo
de factcola es una llamada a la cola.
La ventaja de la recursion de cola es que podemos utilizar el mismo activation record
para todas las llamadas recursivas. Considere la llamada factcola(3,1). La figura 5.1
muestra las partes de cada activation record en el computo que son relevantes para la
discusion.

5.4.1. Ejercicios
5.1. Diagrame los estados de la pila de ejecucion en los diferentes momentos de la ejecucion
del siguiente programa, mostrando como se apilan y desapilan los diferentes activation

46
records a medida que se va ejecutando el programa. Diagrame en dos versiones: una
con un compilador que NO optimice las llamadas a la cola y otra con un compilador
que s las optimice, sin una optimizacion especfica para llamadas recursivas a la
cola, es decir, sin overlay, pero s consiguiendo que el tama
no de la pila se mantenga
constante.
1 def fact (n , acc ) :
if n ==0:
3 return acc
return fact (n -1 , acc * n )
5 fact (4 ,1)

5.5. Alto orden


Mitchell 7.4

5.5.1. Funciones de primera clase


Un lenguaje tiene funciones de primera clase si las funciones pueden ser

declaradas dentro de cualquier alcance,

pasadas como argumentos a otras funciones y,

devueltas como resultado de funciones.

En un lenguaje con funciones de primera clase y con alcance estatico, un valor de


funcion se representa generalmente por una clausura, un par formado por un puntero al
codigo del cuerpo de una funcion y otro puntero a un activation record.
Veamos un ejemplo de funcion en ML que requiere a otra funcion como argumento:
1 fun map (f , nil ) = nil
| map (f , x :: xs ) = f ( x ) :: map (f , xs )
La funcion map toma una funcion f y una lista m como argumentos y aplica f a cada
elemento de m en orden. El resultado de map(f, m) es la lista de resultados f(x) para
elementos x de la lista m. Esta funcion es util en muchos programas en los que se usan
listas. Por ejemplo, si tenemos una lista de tiempos de vencimiento para una secuencia de
eventos y queremos incrementar cada tiempo de vencimiento, podemos hacerlo pasando
una funcion de incremento a map.
Veremos por que se necesitan las clausuras teniendo en cuenta las interacciones entre
el alcance estatico y las funciones como argumentos y como valores de retorno. C y C ++
no admiten clausuras porque tratan de mantener el costo computacional del compilador
al mnimo, y las clausuras involucran un overhead no despreciable. Sin embargo, la imple-
mentacion de objetos en C ++ y otros lenguajes se relaciona con la aplicacion de valores

47
de funcion tal como se explican en este captulo. La razon es que un clausura y un objeto
ambos combinan los datos y el codigo de la funcion.

5.5.2. Pasar Funciones a otras Funciones


Vamos a ver que cuando una funcion f se pasa a una funcion g, es posible que tengamos
que pasar tambien la clausura de f, que contiene un puntero a su activation record. Cuando
f se llama dentro del cuerpo de g, el puntero de entorno de la clausura se utiliza para
determinar cual es el access link que tiene que guardarse en el activation record de la
llamada a f. La necesidad de clausuras en este contexto se conoce como el problema de
downward funarg, porque sucede cuando se pasan funciones como argumentos hacia abajo
en alcances anidados.
Vamos a ilustrar los principales aspectos de este problema con un programa de ejemplo
con dos declaraciones de una variable x y una funcion f que se pasa a otra funcion g:
val x = 4;
2 fun f ( y ) = x * y ;
fun g ( h ) = let val x =7 in h (3) + x ;
4 g(f);
En este programa, el cuerpo de f contiene una variable global x que se declara fuera
del cuerpo de f. Cuando f se llama dentro g, el valor de x se tiene que recuperar desde
el activation record asociado al bloque exterior. De lo contrario el cuerpo de f se eval ua
con la variable x local declarada dentro de g, lo cual es inconsistente con la semantica de
alcance estatico.
Podemos ver el problema de la b usqueda de variables en la pila de ejecucion despues
de la llamada a f desde la invocacion de g.

Esta ilustracion simplificada muestra solo los datos contenidos en cada activation record.
En esta ilustracion, la expresion x * y del cuerpo de f se muestra en la parte inferior, el
activation record asociado a la invocacion de f (a traves del parametro formal h de g).

48
Como muestra la ilustracion, la variable y es local para la funcion y por lo tanto se puede
encontrar en el activation record actual. Sin embargo, la variable x es global, y se encuentra
varios registros de activacion por encima del actual. Para encontrar las variables globales
tenemos que seguir los access links, por lo tanto el access link del activation record inferior
debe permitirnos alcanzar el activation record de la parte superior de la ilustracion.
Cuando las funciones se pasan a otras funciones, hay que guardar el access link para el
registro de la activacion de cada funcion para que podamos encontrar las variables globales
de esa funcion segun la semantica del alcance estatico. Y para poder cubrir este requisito
necesitamos extender alguna estructura de datos de tiempo de ejecucion. Esa estructura
de datos seran las clausuras.
La solucion estandar para mantener el alcance estatico cuando las funciones se pasan
como argumentos a otras funciones o se devuelven como resultados es utilizar una estruc-
tura de datos llamada clausura. La clausura es un par formado por un puntero a la parte de
la memoria donde se guarda el codigo de la funcion y otro puntero a un activation record.
Debido a que cada activation record contiene un access link se nalando al activation record
del bloque del texto del programa mas cercano que lo contiene, un puntero al alcance en
el que se declara una funcion tambien proporciona enlaces al activation record del bloque
que la contiene.
Cuando se pasa como parametro una funcion a otra funcion, el valor real que se pasa es
un puntero a una clausura. A partir de una clausura, una funcion se llama de la siguiente
forma:

5.1. Asignar un activation record para la funcion que se llama, como es habitual.

5.2. Establecer el access link en el activation record utilizando el puntero del activation
record de la clausura.

Podemos ver como esto resuelve el problema de acceso a variables para funciones que
se pasan a otras funciones como argumentos. Para ello vamos a diagramar el estado de la
pila, con los diferentes activation records, cuando se ejecuta el programa anterior, tal como
se muestra en la figura 5.2.
Podemos entender la figura 5.2 atravesando la secuencia de pasos de ejecucion que
conducen a la configuracion que se muestra en la figura:

5.1. Declaracion de x: se apila un activation record para el bloque en el que se declara x.


El activation record contiene un valor para x y un control link que no se muestra.

5.2. Declaracion de f: se apila un activation record para el bloque en ue se declara f. Este


activation record contiene un puntero a la representacion en tiempo de ejecucion de
f, que es una clausura con dos punteros. El primer puntero apunta al activation
record para el alcance estatico de f, que es el activation record para la declaracion de
f. El segundo puntero apunta al codigo de verbf, que fue producido durante la
compilacion y se coloca en alguna ubicacion de memoria que conoce el compilador.

49
Figura 5.2: Como se establecen los access links desde la clausura.

5.3. Declaracion de g: Al igual que con la declaracion de f, se apila un activation record


para el bloque en el que se declaro g. Este activation record contiene un puntero a la
representacion en tiempo de ejecucion de g, que es una clausura.

5.4. Llamada a g(f): La llamada hace que se apile un activation record para la funcion g.
El tamano y estructura de este record los determina el codigo para g. El access link se
establece hacia el activation record para el alcance donde se declara g; el access link
apunta al mismo activation record que el activation record que hay en la clausura de
g. El activation record tiene espacio para el parametro h y la variable local x. Debido
a que el parametro real es la clausura de f, el valor del parametro h es un puntero a
la clausura de f. La variable local x tiene un valor de 7, que viene dado por el codigo
fuente.

5.5. Llamada a h(3): El mecanismo para la ejecucion de esta llamada es el punto principal
de este ejemplo. Debido a que h es un parametro formal de g, el codigo de g se
compila sin saber donde se declara la funcion h. Como resultado, el compilador no
puede establecer el access link para el activation record para la llamada h(3). Sin
embargo, la clausura provee esa informacion: el access link para este activation record
se establece mediante el puntero de activation record de la clausura de h. Debido a
que el parametro real es f, el acces link apunta al activation record para el alcance
en el que se declaro f. Cuando se ejecuta el codigo de f, el access link se utiliza para
encontrar x. En concreto, el codigo seguira el access link hasta el segundo activation
record de la ilustracion, seguira un access link adicional porque el compilador saba,
cuando genero el codigo para f, que la declaracion de x se encuentra un alcance por
encima de la declaracion de f, y encontrara el valor 4 para la variable global x en el
cuerpo de f.

50
Como se describe en el paso 5, la clausura de f hace posible que el codigo que se ejecuta
encuentre el activation record que contiene la declaracion global de x.
Cuando podemos pasar funciones como argumentos, los access links dentro de la pila
forman un arbol. La estructura no es lineal, porque el activation record correspondiente a
la llamada a la funcion h(3) tiene que saltar el activation record para g(f) para encontrar
el x adecuado. Sin embargo, todos los access links apuntan hacia arriba. Por lo tanto, sigue
siendo posible asignar y desasignar activation records mediante el uso de la disciplina de
pila (
ultimo apilado, primero desapilado).

5.6. Excepciones
Mitchell 8.2
Las excepciones proporcionan una forma estructurada de salto que se puede utilizar
para salir de una construccion tal como un bloque o llamada a funcion. El nombre de
excepcion sugiere que las excepciones deben ser utilizados en circunstancias excepcionales.
Sin embargo, es muy habitual usarlas de formas muy poco excepcionales, como por ejemplo:

salir de un bloque o llamada de funcion

pasar datos como parte de un salto

volver a un punto del programa fijado para continuar la computacion

Ademas de saltar de un punto a otro del programa, tambien se asocia con las excepciones
parte del manejo de memoria. Mas en concreto, se pueden desapilar activation records que
ya resulten innecesarios como resultado del salto.
Todo mecanismo de excepcion incluye dos construcciones ling usticas:

levantar, rise o throw, una construccion para ejecutar una excepcion, que corta parte
del computo actual y provoca un salto (transferencia de control),

un controlador, handle o catch, un mecanismo de control, que permite ciertas decla-


raciones, expresiones o llamadas a funcion provistos de codigo para responder a las
excepciones que se lanzan durante la ejecucion.

Hay varias razones por las que las excepciones se han convertido en construcciones muy
aceptadas en la mayor parte de lenguajes. Muchos lenguajes no tienen ning un otro mecanis-
mo limpio para saltar fuera de una llamada de funcion, por ejemplo, abortando la llamada.
En lugar de utilizar instrucciones del tipo go to, que permiten crear codigo totalmente
desestructurado (codigo spaghetti), muchos programadores prefieren usar excepciones, que
se pueden utilizar para saltar solo hacia la parte del programa que ya se ha cargado en la
pila, no hacia alguna parte del programa que no se ha ejecutado todava.
Las excepciones tambien permiten a un programador pasar datos como parte del salto,
lo cual resulta muy util si el programa trata de recuperarse de alg un tipo de condicion

51
de error. Las excepciones proporcionan un metodo dinamico y u til de determinar hasta
donde llega un salto. Si se declara mas de un mecanismo de control, se establece cual es el
controlador adecuado en cada caso siguiendo una semantica de alcance dinamico, lo cual
no sera facil de lograr con otras formas de saltos de control.
La utilidad del alcance dinamico para estos casos se ilustra en el siguiente ejemplo, el
calculo de la inversa de una matriz de n umeros reales. Calculamos la multiplicacion de
matrices, que se utiliza en el algebra lineal, multiplicando las entradas de dos matrices.
Si A es una matriz e I es la matriz identidad (con unos a lo largo de la diagonal y ceros
en todas las otras posiciones), entonces la inversa A1 de A es una matriz tal que el
producto AA1 = I. Una matriz cuadrada tiene una inversa si y solo si algo que se llama
el determinante de A no es igual a cero. Encontrar el determinante de una matriz requiere
aproximadamente el mismo coste computacional que invertir una matriz.
Supongamos que escribimos una funcion para invertir matrices que tienen entradas de
numeros reales. Como solo se pueden invertir matrices con determinantes distintos de cero,
no podemos calcular correctamente la inversa de una matriz si el determinante resulta ser
cero. Una forma de manejar esta dificultad podra ser la de comprobar el determinante
antes de llamar a la funcion de inversion. Pero esto no es una buena idea, porque calcular
el determinante de una matriz es aproximadamente tanto trabajo como invertirla. Como
podemos comprobar facilmente el determinante cuando estamos tratando de convertir a la
inversa, tiene mas sentido tratar de invertir la matriz y lanzar una excepcion en el caso de
que detectemos un determinante cero. Esto lleva a un codigo estructurado de la siguiente
manera:
exception Determinante ; ( * Declarar excepcion Determinante * )
2 fun invertir ( aMatriz ) =
...
4 if ...
then raise Determinante ( * si el determinante de la matriz
es cero * )
6 else ...
end ;
8 invertir ( miMatriz ) handle Determinante ... ;
En este ejemplo, la funcion invertir genera una excepcion si el determinante de
aMatrix resulta ser cero. Si la funcion levanta esta excepcion como resultado de la llamada
invertir(myMatrix), entonces, debido a la declaracion de handle asociada, la llamada
a invertir se aborta y se transfiere el control al codigo que sigue inmediatamente al
handle Determinante.
En este ejemplo, el mecanismo de excepcion se utiliza para manejar una condicion que
hace que sea imposible continuar el calculo. En concreto, si el determinante es cero, la
matriz no tiene inversa, y es imposible para la funcion de inversion devolver la inversa de
la matriz.
Pregunta 7:
Si lo piensas bien, este ejemplo ilustra la necesidad de alg
un tipo de mecanismo

52
de alcance dinamico. Mas especficamente, supongamos que hay varias llamadas
para invertir en un programa que hace calculos matriciales. Cada una de estas
llamadas podran levantar la excepcion Determinante. Cuando una llamada
levanta esta excepcion, donde nos gustara manejar la excepcion?

La motivacion principal para una semantica de alcance dinamico de los handles de ex-
cepcion es que el codigo en el que se llama a la funcion invertir es el mejor lugar para
decidir que hacer si el determinante es cero. Si las excepciones tuvieran alcance estatico,
entonces al lanzar una excepcion se saltara al controlador de excepcion que se declaro lexi-
camente (en el texto del programa) antes de la definicion de la funcion invert. Si invert
esta en una librera de programas, entonces el controlador tendra que estar tambien en la
librera. Sin embargo, la persona que escribio la librera no tiene idea de que hacer en los
casos en que se produce la excepcion. Por este motivo, las excepciones estan en el alcance
de forma dinamica: cuando se produce una excepcion, el control salta al controlador que
se establecio por ultima vez en la historia dinamica de llamadas del programa. Vamos a
ver como funciona esto con mas detalle en algunos mecanismos de excepcion especficos.

5.6.1. Ejercicios
5.1. La parte de las excepciones que tiene alcance dinamico es...

a) el declare.
b) el raise.
c) el handle.

5.2. Se usa alcance dinamico en las excepciones...

a) para no colisionar con el alcance de las variables.


b) porque se busca encontrar un patron recorriendo la pila de ejecucion.
c) porque el programador tiene mas claro que quiere que quien programo las li-
breras.

5.3. Las excepciones se pueden usar para optimizar codigo...

a) porque tienen alcance dinamico.


b) porque son saltos explcitos entre partes del programa.
c) porque son imperativas.
d) porque son funcionales.

5.4. En las siguientes funciones en ML:

53
exception Excpt of int ;
2 fun twice (f , x ) = f ( f ( x ) ) handle Excpt ( x ) = > x ;
fun pred ( x ) = if x = 0 then raise Excpt ( x ) else x -1;
4 fun dumb ( x ) = raise Excpt ( x ) ;
fun smart ( x ) = 1 + pred ( x ) handle Excpt ( x ) = > 1;

Cual es el resultado de evaluar cada una de las siguientes expresiones?

a) twice(pred,1)
b) twice(dumb,1)
c) twice(smart,1)

Explique que excepcion se levanta en cada caso y donde se levanta.

5.5. En el siguiente codigo java, inserte en el main los mecanismos de manejo de excep-
ciones necesarios para capturar la excepcion de forma adecuada.
1 public class RepartirComensales
{
3 public static void main ( String [] args )
{
5 Mesa mesa = new Mesa (1) ;
System . out . println ( " vamos a llenar la mesa 1 ... " ) ;
7 mesa . aniadirComensal ( " Ana " ) ;
mesa . aniadirComensal ( " Juan " ) ;
9 mesa . aniadirComensal ( " Maria " ) ;
mesa . aniadirComensal ( " Pedro " ) ;
11 mesa . aniadirComensal ( " Juana " ) ;
mesa . aniadirComensal ( " Esteban " ) ;
13 mesa . aniadirComensal ( " Lola " ) ;
}
15 }

17 public class Mesa


{
19 private int numeroDeComensales ;
private int numeroDeMesa ;
21 public Mesa ( int numeroDeMesa )
{
23 this . numeroDeMesa = numeroDeMesa ;
}
25 public void aniadirComensal ( string comensal ) throws
ExcepcionMesaLlena

54
{
27 if ( numeroDeComensales > 5)
{
29 throw new ExcepcionMesaLlena ( numeroDeComensales )
}
31 else
{
33 numeroDeComensales += 1;
}
35 }
}

5.6. Escriba el siguiente programa con excepciones en lugar de tratar manejo de errores
mediante estructuras de control.
Nota: no es necesario que el programa compile, puede usar pseudocodigo siempre que su
semantica sea inambigua.
codigoErrorType readFile {
2 initialize codigoError = 0;

4 // abrir el archivo ;
if ( elArchivoEstaAbierto ) {
6 // determinar la longitud del archivo ;
if ( obtenemosLongitudArchivo ) {
8 // alojar esa cantidad de memoria ;
if ( tenemosSuficienteMemoria ) {
10 // leemos el archivo a memoria ;
if ( falloLectura ) {
12 codigoError = -1;
}
14 } else {
codigoError = -2;
16 }
} else {
18 codigoError = -3;
}
20 // cerrar el archivo ;
if ( elArchivoNoSeCerro && codigoError == 0) {
22 codigoError = -4;
} else {
24 codigoError = codigoError and -4;
}
26 } else {

55
codigoError = -5;
28 }
return codigoError ;
30 }

5.7. En el siguiente codigo java, inserte en el main los mecanismos de manejo de excep-
ciones necesarios para capturar la excepcion de forma adecuada.
public class BankDemo
2 {
public static void main ( String [] args )
4 {
CuentaBancaria c = new CuentaBancaria (101) ;
6 System . out . println ( " Depositando $500 ... " ) ;
c . depositar (500.00) ;
8 System . out . println ( " \ nRetirando $100 ... " ) ;
c . retirar (100.00) ;
10 System . out . println ( " \ nRetirando $600 ... " ) ;
c . retirar (600.00) ;
12 }
}

1 public class CuentaBancaria


{
3 private double balance ;
private int numero ;
5 public CuentaBancaria ( int numero )
{
7 this . numero = numero ;
}
9 public void depositar ( double cantidad )
{
11 balance += cantidad ;
}
13 public void retirar ( double cantidad ) throws
ExcepcionFondosInsuficientes
{
15 if ( cantidad <= balance )
{
17 balance -= cantidad ;
}
19 else
{
21 double necesita = cantidad - balance ;

56
throw new ExcepcionFondosInsuficientes ( necesita ) ;
23 }
}
25 public double getBalance ()
{
27 return balance ;
}
29 public int getNumber ()
{
31 return numero ;
}
33 }

5.7. Recolecci
on de basura (garbage collection)
5.7.1. Ejercicios
5.1. Para los puntos en el codigo marcados con (A), (B) y (D) reporte que variables
pueden ser recolectadas por el recolector de basura.
1 local X Y Z B R
fun { M X }
3 proc { R Y } Y = X end
end
5 P ={ M X }
Q ={ M Y } % (A)
7 R=P
in
9 X =2
local A B C R = Q Z = foo ( A f1 : B C ) in
11 {R Z} % (B)
{ Browse Z } % (C)
13 end
{R Z} % (D)
15 Y =3
{ Browse Z } % (E)
17 end

5.2. En los puntos indicandos con <----, indique cuales son las variables que pueden ser
recolectadas si el recolector de basura (garbage collector) se ejecuta.
1 fun { Mergesort Xs }
case Xs

57
3 of nil then nil
[] [ X ] then [ X ]
5 else Ys Zs in
{ Split Xs Ys Zs }
7 <----
{ Merge { Mergesort Ys }
9 { Mergesort Zs }}
end
11 end

13 Z = [1 2 3 4 5]
XS = { Mergesort 1 | 2 | 3 | Z }
15 { Browse Z }
<----
17 { Browse XS }
<----

5.3. Escriba en pseudocodigo un ejemplo de programa donde hay variables que se pueden
recoletar semanicamente pero no sintacticamente.

5.4. Escriba en pseudocodigo un ejemplo de programa donde hay variables que, aunque
por la estructura de bloques se podran recolectar, no se pueden recolectar porque
tienen una referencia de otra variable. Luego, modifique el programa anterior inser-
tando una sentencia dereference que elimine todas las referencias a una variable.
Ahora, la variable quedara disponible para recoleccion de basura? Que problema pue-
de acarrear el uso de una sentencia como dereference? Compare ese problema con
el problema de los memory leaks, y opine sobre la gravedad de cada uno de ellos.

5.5. En el siguiente codigo en Ruby, cuando se puede recolectar la memoria asociada a


obj?
module Base
2 def click ( element_hash )
begin
4 obj =
@browser . element ( element_hash . keys [0] , element_hash . values [0])
obj . when_present . click
6 report_pass ( " Element successfully clicked " )
rescue = > exception
8 report_fail ( " Failed to click on object . #{ exception })
end
10 end

58
5.6. En el siguiente programa en Java, que variables pueden ser recolectadas por el re-
colector de basura, y cuando? En su explicacion, use el concepto de alcanzabilidad.
NOTA: En Java, obj = null elimina exactamente una referencia a un objeto.
class A { int i = 5; }
2 class B { A a = new A () ; }
class C {
4 B b;
public static void main ( String args []) {
6 C c = new C () ;
c.b = new B () ;
8 c.b = null ;
}

5.7. Compare el overhead que se da en Garbage Collection con el que se da en Orientacion


a Objetos. En un caso, el overhead se encuentra en el acceso a estructuras de datos
en memoria, en otro caso el overhead se da por cambios de contexto y procesos de
sistema. Cual cree que proporciona un mayor beneficio en eficacia del programador,
cual cree que contribuye a la naturalidad para expresar soluciones informaticas, cual
cree que evita errores de programacion, y por que?

5.8. El metodo finalize() en Java contiene codigo que se ejecuta cuando el recolector
de basura reclama a un objeto. La accion obj = null; elimina una referencia a un
objeto. Cual de estas dos acciones contribuye de forma mas efectiva a la liberacion
de memoria del programa?

5.9. Comente el siguiente texto, explicando primero cual es la funcion del garbage co-
llector en un lenguaje de programacion, y describiendo los problemas que se pueden
encontrar con el uso de finalize().

Many GC systems have a notion of finalization..An object may be regis-


tered with the GC system so that when it is about to reclaim the object, it
runs a function on the object that can perform necessary cleanups. Finali-
zation is tricky. Some of the issues are:
When can an object actually be finalized? This is trickier than it first ap-
pears in the presence of some normally-desirable optimizing transforma-
tions.
In what thread, resource, or security context does a finalization function
run?
What happens when registered objects reference each other?
What happens if a finalization function makes an object not be garbage any
more?

59
5.10. El metodo finalize() en Java se ejecuta cuando el recolector de basura reclama
un objeto. En el siguiente pedazo de codigo tenemos el uso del metodo cleanup.
Imaginemos que tanto cleanup() como finalize() se usan para liberar los recursos
asociados a myObj, por ejemplo, file handlers. Entonces, en cual de los dos casos
sabemos con exactitud en que momento se liberan los recursos asociados a myObj?
1 MyClass myObj ;

3 myObj = new MyClass () ;


// ...
5 myObj . finalize () ;

1 MyClass myObj ;

3 try {
myObj = new MyClass () ;
5

// ...
7 } finally {

9 if ( null != myObj ) { // no hay ninguna referencia a


myObj
myObj . cleanup () ;
11 }
}

60
Captulo 6

Paradigma funcional

Mitchell 4.4.
Van Roy y Haridi. 2004. Concepts, Techniques, and Models of Computer Programming.
MIT Press. Captulo 3: introduccion

6.1. Expresiones imperativas vs. expresiones funcio-


nales
En muchos lenguajes de programacion, las construcciones basicas son imperativas, del
mismo modo que lo sera una oracion imperativa en lenguaje natural, como por ejemplo
Traeme esa manzana.. Por ejemplo, esta instruccion de asignacion:
x: = 5
es una orden que el programador le da a la computadora para guardar el valor 5 en un
lugar determinado.
Los lenguajes de programacion tambien contienen construcciones declarativas, como la
declaracion de la funcion
1 function f ( int x ) { return x +1; }
que describe un hecho, de la misma forma que en lenguaje natural podramos decir La
tierra es redonda.. En este ejemplo, lo que describimos es que f es una funcion cuyo valor
de retorno es 1 mayor que su argumento..
La distincion entre construcciones imperativas y declarativas se basa en que las impe-
rativas cambian un valor y las declarativas declaran un nuevo valor. Por ejemplo, en el
siguiente fragmento de programa:
1 { int x = 1;
x = x +1;
3 { int y = x +1;
{ int x = y +1;
5 }}}

61
solo la segunda lnea es una operacion imperativa, las otras lneas contienen declaraciones
de nuevas variables.
Un punto sutil es que la u
ltima lnea en el codigo anterior declara una nueva variable con
el mismo nombre que el de una variable declarada anteriormente. La forma mas sencilla de
entender la diferencia entre declarar una nueva variable y cambiar el valor de una variable
ya existente es cambiando el nombre de la variable. Las variables ligadas, que no son libres
en una expresion (que estan definidas dentro del alcance de la expresion) pueden cambiar
de nombre sin cambiar el significado de la expresion. En particular, podemos cambiar
el nombre de las variables ligadas en el fragmento de programa anterior de la siguiente
manera:
1 { int x = 1;
x = x +1;
3 { int y = x +1;
{ int z = y +1;
5 }}}
(Si hubiera mas ocurrencias de x dentro del bloque interior, tambien les cambiaramos el
nombre a z.) Despues de volver a escribir el programa a esta forma equivalente, podemos
ver facilmente que la declaracion de una nueva variable z no cambia el valor de cualquier
variable ya existente.
La asignacion imperativa puede introducir efectos secundarios porque puede destruir
el valor anterior de una variable, sustituyendolo por uno nuevo, de forma que este no
este disponible mas adelante. En programacion funcional la asignacion imperativa se conoce
como asignacion o actualizacion destructiva.
Decimos que una operacion computacional es declarativa si, cada vez que se invoca con
los mismos argumentos, devuelve los mismos resultados independientemente de cualquier
otro estado de computacion. Una operacion declarativa es:
independiente no depende de ning
un estado de la ejecucion por fuera de s misma,
sin estado no tiene estados de ejecucion internos que sean recordados entre invocaciones,
y
determinstica siempre produce los mismos resultados para los mismos argumentos y,
por extension, ejecutar la operacion no tendra efectos secundarios.
Una consecuencia muy valiosa de estas propiedades es la conocida como transparencia
referencial. Una expresion es transparente referencialmente si se puede sustituir por su valor
sin cambiar la semantica del programa. Esto hace que todas las componentes declarativas,
incluso las mas complejas, se puedan usar como valores en un programa, por ejemplo, como
argumentos de funcion, como resultados de funcion o como partes de estructuras de datos.
Esto aporta una gran flexibilidad a los lenguajes funcionales, ya que se pueden tratar de
forma homogenea expresiones de estructura muy variable, como por ejemplo en el siguiente
programa:

62
1 HayAlgunExceso xs n = foldr ( filter ( > n ) ( map
convertirSistemaMetrico xs ) ) False xs
Veamos un ejemplo de dos funciones, una referencialmente transparente y otra referen-
cialmente opaca:
1 globalValue = 0;

3 integer function rq ( integer x )


begin
5 globalValue = globalValue + 1;
return x + globalValue ;
7 end

9 integer function rt ( integer x )


begin
11 return x + 1;
end
La funcion rt es referencialmente transparente, lo cual significa que rt(x) = rt(y) si x
= y. Sin embargo, no podemos decir lo mismo de rq porque usa y modifica una variable
global. Por ejemplo, si queremos razonar sobre la siguiente asercion:
integer p = rq ( x ) + rq ( y ) * ( rq ( x ) - rq ( x ) ) ;
Podramos querer simplificarla de la siguiente forma:
1 integer p = rq ( x ) + rq ( y ) * (0) ;
integer p = rq ( x ) + 0;
3 integer p = rq ( x ) ;
Pero esto no es valido para rq() porque cada ocurrencia de rq(x) se eval ua a un valor
distinto. Recordemos que el valor de retorno de rq esta basado en una variable global que
no se pasa como parametro de la funcion y que se modifica en cada llamada a rq. Esto
implica que no podemos asegurar la veracidad de aserciones como x x = 0.
Por lo tanto, la transparencia referencial nos permite razonar sobre nuestro codigo de
forma que podemos construir programas mas robustos, podemos encontrar errores que no
podramos haber encontrado mediante testing y podemos encontrar posibles optimizaciones
que podemos trasladar al compilador.

6.2. Propiedades valiosas de los lenguajes funcionales


Los lenguajes funcionales, como Lisp o ML, realizan la mayor parte del computo me-
diante expresiones con declaraciones de funciones. Todos los lenguajes funcionales tienen
tambien construcciones imperativas, pero en esos lenguajes se pueden escribir programas
muy completos sin usarlas.

63
En algunos casos se usa el termino lenguaje funcional para referirse a lenguajes
que no tienen expresiones con efectos secundarios o cualquier otra forma de construccion
imperativa. Para evitar ambig uedades entre estos y lenguajes como Lisp o ML, llamaremos
a estos u
ltimos lenguajes funcionales puros. Los lenguajes funcionales puros pueden pasar
el siguiente test:
Dentro del alcance de las declaraciones x1 , ..., xn , todas las ocurrencias de una
expresion e que contenga solo las variables x1 , ..., xn tendran el mismo valor.
Como consecuencia de esta propiedad, los lenguajes funcionales puros tienen una propiedad
muy u til: si la expresion e ocurre en varios lugares dentro de un alcance especfico, entonces
la expresion solo necesita evaluarse una vez. Esto permite que el compilador pueda hacer
optimizaciones de calculo y de espacio en memoria (aunque no necesariamente todos los
compiladores de todos los lenguajes funcionales lo van a hacer).
Otra propiedad interesante de los programas declarativos es que son composicionales.
Un programa declarativo consiste de componentes que pueden ser escritos, comprobados,
y probados correctos independientemente de otros componentes y de su propia historia
pasada (invocaciones previas).
Otra propiedad interesante es que razonar sobre programas declarativos es sencillo. Es
mas facil razonar sobre programas escritos en el modelo declarativo que sobre programas
escritos en modelos mas expresivos. Como los programas declarativos solo pueden calcular
valores, se pueden usar tecnicas sencillas de razonamiento algebraico y logico.
En un programa declarativo, la interaccion entre componentes se determina u nicamente
por las entradas y salidas de cada componente. Considere un programa con un componente
declarativo. Este componente puede ser mirado en s mismo, sin tener que entender el resto
del programa. El esfuerzo que se necesita para entender el programa completo es la suma
de los esfuerzos que se necesitan para entender el componente declarativo y para entender
el resto.
Si existiera una interaccion mas estrecha entre el componente y el resto del programa
no se podran entender independientemente. Tendran que entenderse juntos, y el esfuerzo
requerido sera mucho mas grande. Por ejemplo, podra ser (aproximadamente) proporcio-
nal al producto de los esfuerzos requeridos para entender cada parte. En un programa con
muchos componentes que interactuan estrechamente, esto explota muy rapido, difcultan-
do o haciendo imposible entenderlo. Un ejemplo de interaccion estrecha es un programa
concurrente con estado compartido.
Pero para algunos problemas, es necesario trabajar con interacciones estrechas. Sin
embargo, podemos adoptar por principio la directiva de tratar de hacer codigo lo mas
modular posible, con interacciones estrechas solo cuando sea necesario y no en cualquier
caso. Para soportar este principio, el total de componentes declarativos debera ser el mayor
numero posible.
Estas dos propiedades son importantes tanto para programar en grande como en pe-
queno. Sera muy agradable si todos los programas pudieran escribirse en el modelo de-
clarativo. Desafortunadamente, este no es el caso. El modelo declarativo encaja bien con
ciertas clases de programas y mal con otras.

64
6.3. Problemas naturalmente no declarativos
La forma mas elegante y natural (compacta e intuitiva) de representar algunos proble-
mas es mediante estado explcito. Es el caso, por ejemplo, de las aplicaciones cliente-servidor
y de las aplicaciones que muestran video. En general, todo programa que realice alg un tipo
de Input-Output lo hara de forma mas natural mediante estado explcito. Tambien es el
caso en el que estamos tratando de modelar alg un tipo de comportamiento (por ejemplo,
agentes inteligentes, juegos) en el que representamos a una componente de software como
una entidad que tiene memoria, porque inherentemente la memoria cambia a lo largo del
tiempo.

Pregunta 1:
Piense algunos ejemplos de problemas en los que el estado debera estar repre-
sentado en la solucion de software de forma explcita.

Tambien hay tipos de problemas que se pueden programar de forma mucho mas eficiente
si se hace de forma imperativa. En esos casos podemos llegar a convertir un problema
que es intratable con programacion declarativa en un problema tratable. Este es el
caso de un programa que realiza modificaciones incrementales de estructuras de datos
grandes, e.g., una simulacion que modifica grafos grandes, que en general no puede ser
compilado eficientemente. Sin embargo, si el estado esta guardado en un acumulador y el
programa nunca requiere acceder a un estado ya pasado, entonces el acumulador se puede
implementar con asignacion destructiva (ver Van Roy y Haridi 6.8.4.).
Por esta razon muchos lenguajes funcionales proveen alg un tipo de construccion ling
usti-
ca para poder expresar instrucciones imperativas. Incluso en lenguajes funcionales puros
existen estas construcciones, que en general se conocen como monadas. Las monadas son
una construccion de un lenguaje que permite crear un alcance aislado del resto del pro-
grama. Dentro de ese alcance, se permiten ciertas operaciones con efectos secundarios, por
ejemplo, el uso de variables globales (globales al alcance) o asignacion destructiva. Esta ga-
rantizado que estos efectos secundarios no van a afectar a la parte del programa que queda
fuera del alcance de la monada. Las monadas han sido descritas como un punto y coma
programable, que transportan datos entre unidades funcionales que los van transformando
un paso a la vez.
Sabiendo todo esto, podemos considerar que la programacion declarativa es eficien-
te? Existe una distancia muy grande entre el modelo declarativo y la arquitectura de una
computadora. La computadora esta optimizada para modificar datos en su lugar, mientras
que el modelo declarativo nunca modifica los datos sino que siempre crea datos nuevos.
Pero sin embargo esto no es un problema tan grave como podra parecer, porque el com-
pilador puede convertir partes importantes de los programas funcionales en instrucciones
imperativas.

Pregunta 2:
Piense dos ejemplos de operacion declarativa y dos ejemplos de operacion no
declarativa

65
Pregunta 3:
Piense por lo menos una operacion que no se pueda llevar a cabo sin estado

6.4. Concurrencia declarativa


Una consecuencia muy valiosa de los programas escritos de forma funcional pura, sin
expresiones con efectos secundarios, es que se pueden ejecutar de forma concurrente con
otros programas con la garanta de que su semantica permanecera inalterada. De esta forma,
paralelizar programas funcionales puros es trivial. En cambio, para paralelizar programas
imperativos, es necesario detectar primero las posibles regiones del programa donde puede
haber condiciones de carrera, y, si es necesario, establecer condiciones de exclusion m utua.
Backus acu no el termino el cuello de botella von Neumann (von Neumann bottleneck)
para referirse al hecho de que cuando ejecutamos un programa imperativo, la computacion
funciona de a un paso por vez. Como es posible que cada paso en un programa dependa
del previo, tenemos que pasar el estado de la memoria (asignacion de valores a variables)
de la memoria a la CPU cada vez que ejecutamos un paso de la computacion. Este canal
secuencial entre la CPU y la memoria es el cuello de botella von Neumann.
Sin embargo, aunque paralelizar programas funcionales sea trivial, es difcil aprovechar
este potencial en la practica. Efectivamente, en algunos puntos de un programa tiene sentido
introducir paralelismo y en otros no. El paralelismo tiene un peque no coste (overhead) en
la creacion de nuevos procesos, incluso si son ligeros, y tambien en el posible cambio de
contexto si hay un solo procesador. Por otro lado, si la ejecucion de un proceso depende del
resultado de otro, no se obtiene ning un beneficio al paralelizarlos. Es necesario entonces
hacer algun tipo de analisis previo para que la paralelizacion de los programas declarativos
sea realmente efectiva.

6.5. Ejercicios
6.1. Cual de estas dos funciones, rq o rt, es transparente referencialmente?
1 globalValue = 0;

3 integer function rq ( integer x )


begin
5 globalValue = globalValue + 1;
return x + globalValue ;
7 end

9 integer function rt ( integer x )


begin
11 return x + 1;
end

66
6.2. El siguiente pedazo de codigo es declarativo? Por que?
{ int x = 1;
2 x = x +1;
{ int y = x +1;
4 { int x = y + z ;
}}}

6.3. En los siguientes programas imperativos, identifique porciones de codigo que no sean
declarativas.
1 int A ;
int B ;
3

int Add ()
5 {
return A + B ;
7 }

9 int main ()
{
11 int answer ; // Local variable
A = 5;
13 B = 7;
answer = Add () ;
15 printf ( " %d \ n " , answer ) ;
return 0;
17 }

1 int glob = 0;

3 int square ( int x )


{
5 glob = 1;
return x * x ;
7 }

9 int main ()
{
11 int res ;
glob = 0;
13

67
res = square (5) ;
15 res += glob ;

17 return res ;
}

6.4. Reescriba el siguiente codigo en Java en un lenguaje declarativo, por ejemplo Haskell
o pseudocodigo declarativo, de forma que su semantica denotacional sea identica.
class P01 {
2

<T > T f1 ( List <T > list ) {


4 if ( list . isEmpty () ) throw new
NoHayElemento ( " lista vacia " ) ;
List <T > elements = list . tail () ;
6 List <T > result = list ;
while ( elements . nonEmpty () ) {
8 result = elements ;
elements = elements . tail () ;
10 }
return result . head () ;
12 }

6.5. En un programa imperativo cualquiera, identifique porciones de codigo que no sean


declarativas.

6.6. En un programa imperativo cualquiera, identifique posibles canales por los que even-
tualmente propagarse efectos secundarios.

68
Captulo 7

Orientaci
on a objetos

Mitchell 10., 11., 12.1, 12.2, 12.3, 13.1, 13.2

7.1. Ejercicios
Seleccione la(s) respuesta(s) correcta(s)
7.1. La diferencia entre interfaces de Java y clases abstractas de C++ es que...

a) las clases abstractas de C++ permiten herencia m


ultiple.
b) las interfaces de Java no pueden heredar de ninguna clase.
c) las interfaces de Java permiten herencia m
ultiple.
d) las clases abstractas de C++ no pueden ser instanciadas.

7.2. En programacion orientada a objetos, la interfaz es la parte de un objeto...

a) que se ve en la pantalla.
b) que se comunica con el resto del programa.
c) que puede ser heredada por mas de una clase.

7.3. Tenemos un name clash cuando...

a) una clase hereda dos miembros con el mismo nombre de dos de sus madres.
b) las signaturas de dos clases cualquiera tienen un miembro en com
un.

7.4. Se dice que en Java no hay funciones porque...

a) no es un lenguaje funcional.
b) no se pueden encontrar funciones fuera de los objetos.
c) se llaman metodos.

69
7.5. La diferencia entre extends e implements en Java consiste en que...

a) extends crea relaciones de herencia, mientras que implements crea relaciones


de implementacion.
b) extends se aplica para heredar de clases, mientras que implements se aplica
para heredar de interfaces.
c) extends solo permite herencia simple, mientras que implements permite heren-
cia m
ultiple.

7.6. Los objetos se pueden considerar como...

a) instancias de clases.
b) ejecuciones de una funcion que es la clase.
c) activation records de una clase.

7.7. Muchos lenguajes proveen sintaxis amigable para referirse a algunas clases dentro de
la jerarqua de clases, que son:

a) las clases hermanas de la clase desde las que se las refiere.


b) la clase madre de la clase desde que se la refiere.
c) las clases ancestro de la clase desde la que se esta trabajando.
d) la propia clase desde la que se esta trabajando.
e) las clases descendientes de la clase desde la que se esta trabajando.

7.8. En las siguientes declaraciones de clase, indique que partes son implementacion y
que partes son interfaz, marcando la interfaz.
public class URLExpSimple {
2

public static void main ( String [] args ) {


4 try {
URL mySite = new
URL ( " http :// www . cs . utexas . edu /~ scottm " ) ;
6 URLConnection yc = mySite . openConnection () ;
Scanner in = new Scanner ( new
InputStreamReader ( yc . getInputStream () ) ) ;
8 int count = 0;
while ( in . hasNext () ) {
10 System . out . println ( in . next () ) ;
count ++;
12 }
System . out . println ( " Number of tokens : " +
count ) ;

70
14 in . close () ;
} catch ( Exception e ) {
16 e . printStackTrace () ;
}
18 }
}

1 public class Stopwatch


{
3 private long startTime ;
private long stopTime ;
5

public static final double NANOS_PER_SEC =


1000000000.0;
7

public void start ()


9 { startTime = System . nanoTime () ; }

11 public void stop ()


{ stopTime = System . nanoTime () ; }
13

public double time ()


15 { return ( stopTime - startTime ) / NANOS_PER_SEC ; }

17 public String toString () {


return " elapsed time : " + time () + " seconds . " ;
19 }

21 public long timeInNanoseconds ()


{ return ( stopTime - startTime ) ; }
23 }

1 public class MineSweeper


{ private int [][] myTruth ;
3 private boolean [][] myShow ;

5 public void cellPicked ( int row , int col )


{ if ( inBounds ( row , col ) && ! myShow [ row ][ col ] )
7 { myShow [ row ][ col ] = true ;

9 if ( myTruth [ row ][ col ] == 0)


{ for ( int r = -1; r <= 1; r ++)
11 for ( int c = -1; c <= 1; c ++)

71
cellPicked ( row + r , col + c ) ;
13 }
}
15 }

17 public boolean inBounds ( int row , int col )


{ return 0 <= row && row < myTruth . length && 0 <= col
&& col < myTruth [0]. length ;
19 }
}

public class PrimeEx {


2

public static void main ( String [] args ) {


4 printTest (10 , 4) ;
printTest (2 , 2) ;
6 printTest (54161329 , 4) ;
printTest (1882341361 , 2) ;
8 printTest (36 , 9) ;

10 System . out . println ( isPrime (54161329) + " expect false " ) ;


System . out . println ( isPrime (1882341361) + " expect
true " ) ;
12 System . out . println ( isPrime (2) + " expect true " ) ;
int numPrimes = 0;
14 Stopwatch s = new Stopwatch () ;
s . start () ;
16 for ( int i = 2; i < 10000000; i ++) {
if ( isPrime ( i ) ) {
18 numPrimes ++;
}
20 }
s . stop () ;
22 System . out . println ( numPrimes + " " + s ) ;
s . start () ;
24 boolean [] primes = getPrimes (10000000) ;
int np = 0;
26 for ( boolean b : primes )
if ( b )
28 np ++;
s . stop () ;
30 System . out . println ( np + " " + s ) ;

72
32 System . out . println ( new BigInteger (1024 , 10 , new
Random () ) ) ;
}
34

public static boolean [] getPrimes ( int max ) {


36 boolean [] result = new boolean [ max + 1];
for ( int i = 2; i < result . length ; i ++)
38 result [ i ] = true ;
final double LIMIT = Math . sqrt ( max ) ;
40 for ( int i = 2; i <= LIMIT ; i ++) {
if ( result [ i ]) {
42 // cross out all multiples ;
int index = 2 * i ;
44 while ( index < result . length ) {
result [ index ] = false ;
46 index += i ;
}
48 }
}
50 return result ;
}
52

54 public static void printTest ( int num , int


expectedFactors ) {
Stopwatch st = new Stopwatch () ;
56 st . start () ;
int actualFactors = numFactors ( num ) ;
58 st . stop () ;
System . out . println ( " Testing " + num + " expect " +
expectedFactors + " , " +
60 " actual " + actualFactors ) ;
if ( actualFactors == expectedFactors )
62 System . out . println ( " PASSED " ) ;
else
64 System . out . println ( " FAILED " ) ;
System . out . println ( st . time () ) ;
66 }

68 // pre : num >= 2


public static boolean isPrime ( int num ) {
70 assert num >= 2 : " failed precondition . num must be >=
2. num : " + num ;

73
final double LIMIT = Math . sqrt ( num ) ;
72 boolean isPrime = ( num == 2) ? true : num % 2 != 0;
int div = 3;
74 while ( div <= LIMIT && isPrime ) {
isPrime = num % div != 0;
76 div += 2;
}
78 return isPrime ;
}
80

// pre : num >= 2


82 public static int numFactors ( int num ) {
assert num >= 2 : " failed precondition . num must be >=
2. num : " + num ;
84 int result = 0;
final double SQRT = Math . sqrt ( num ) ;
86 for ( int i = 1; i < SQRT ; i ++) {
if ( num % i == 0) {
88 result += 2;
}
90 }
if ( num % SQRT == 0)
92 result ++;
return result ;
94 }

7.9. En los siguientes programas escritos en C++, indique que parte del programa se
encontrara en el archivo .h
El archivo .h o archivo de cabeceras contiene declaraciones de clases cuya implementaci on
se encuentra en otros archivos, con extension .cpp. Su finalidad es ser includos en otros
archivos que usan las funciones que se declaran en el archivo de cabeceras. Por ejemplo, en
el archivo .h tendramos:
class A2DD
2 {
private :
4 int gx ;
int gy ;
6

public :
8 A2DD ( int x , int y ) ; // se dejan aca las declaraciones
int getSum () ;
10 };

74
Y en el archivo a2dd.cpp escribimos la implementacion:
A2DD :: A2DD ( int x , int y ) // las definiciones se prefijan con
el nombre de la clase
2 {
gx = x ;
4 gy = y ;
}
6

int A2DD :: getSum ()


8 {
return gx + gy ;
10 }

// include header file with the class declaration


2 # include " frac . hpp "

4 // include standard header files


# include < iostream >
6 # include < cstdlib >

8 / * constructor por defecto , inicializando en 0 * /


Fraction :: Fraction ()
10 : numer (0) , denom (1)
{
12 }

14 / * constructor para un numero entero , inicializando en n


*/
Fraction :: Fraction ( int n )
16 : numer ( n ) , denom (1)
{
18 }

20 / * constructor para numerador y denominador ,


inicializados segun parametro * /
Fraction :: Fraction ( int n , int d )
22 : numer ( n ) , denom ( d )
{
24 // no permitimos 0 como denominador
if ( d == 0) {
26 // salir del programa con un mensaje de error
std :: cerr << " error : el denominador es 0" <<
std :: endl ;

75
28 std :: exit ( EXIT_FAILURE ) ;
}
30 }

32 / * print * /
void Fraction :: print ()
34 {
std :: cout << numer << / << denom << std :: endl ;
36 }

# include " Circle . h " // cabecera definida por usuario en


el mismo directorio
2

// Constructor
4 // los valores por defecto solo se especifican en la
declaracion ,
// no se repiten en la definicion
6 Circle :: Circle ( double r , string c ) {
radius = r ;
8 color = c ;
}
10

// getter publico para el miembro de datos privado


radius
12 double Circle :: getRadius () const {
return radius ;
14 }

16 // setter publico para el miembro de datos privado


radius
void Circle :: setRadius ( double r ) {
18 radius = r ;
}
20

// getter publico para el miembro de datos privado


color
22 string Circle :: getColor () const {
return color ;
24 }

26 // setter publico para el miembro de datos privado


color
void Circle :: setColor ( string c ) {

76
28 color = c ;
}
30

// una funcion miembro publica


32 double Circle :: getArea () const {
return radius * radius * 3.14159265;
34 }

# include < iostream >


2 # include < iomanip >
# include " Time . h " // incluye la cabecera de la clase
Time
4

// Constructor con valores por defecto , sin validacion de


input
6 Time :: Time ( int h , int m , int s ) {
hour = h ;
8 minute = m ;
second = s ;
10 }

12 // getter publico para el miembro de datos privado


hour
int Time :: getHour () const {
14 return hour ;
}
16

// setter publico para el miembro de datos privado


hour , sin validacion de input
18 void Time :: setHour ( int h ) {
hour = h ;
20 }

22 // getter publico para el miembro de datos privado


minute
int Time :: getMinute () const {
24 return minute ;
}
26

// setter publico para el miembro de datos privado


minute , sin validacion de input
28 void Time :: setMinute ( int m ) {
minute = m ;

77
30 }

32 // getter publico para el miembro de datos privado


second
int Time :: getSecond () const {
34 return second ;
}
36

// setter publico para el miembro de datos privado


second , sin validacion de input
38 void Time :: setSecond ( int s ) {
second = s ;
40 }

42 // Fijar hora , minuto y segundo , sin validacion de input .


void Time :: setTime ( int h , int m , int s ) {
44 hour = h ;
minute = m ;
46 second = s ;
}
48

// Imprimir esta instancia de Time en el formato de


" hh : mm : ss "
50 void Time :: print () const {
cout << setfill ( 0 ) ; // zero - filled , need
< iomanip > , sticky
52 cout << setw (2) << hour // set width to 2 spaces ,
need < iomanip > , non - sticky
<< " : " << setw (2) << minute
54 << " : " << setw (2) << second << endl ;
}
56

// Aumentar esta instancia por un segundo


58 void Time :: nextSecond () {
++ second ;
60 if ( second >= 60) {
second = 0;
62 ++ minute ;
}
64 if ( minute >= 60) {
minute = 0;
66 ++ hour ;
}

78
68 if ( hour >= 24) {
hour = 0;
70 }
}

7.10. Dibuje la jerarqua de clases en que se basa el siguiente codigo.


1 class storable
{
3 public :
storable ( const char * ) ;
5 virtual void read () =0; // this becomes pure
virtual making storable an abstract
virtual void write () ; // class
7 virtual ~ storable () ;
private :
9 ... .
}
11

class transmitter : public virtual storable


13 {
public :
15 void write ()
{
17 read () ;
... .
19 }
}
21

class receiver : public virtual storable


23 {
public :
25 void read () ;
}
27

class radio : public transmitter , public receiver


29 {
public :
31 ...
}
33

int main ()
35 {

79
radio * rad = new radio () ;
37 receiver * r1 = rad ;
transmitter * r2 = rad ;
39

rad - > write () ;


41 r1 - > write () ;
r2 - > write () ;
43 return 1;
}

SpinSpots spots ;
2 SpinArm arm ;

4 void setup () {
size (640 , 360) ;
6 arm = new SpinArm ( width /2 , height /2 , 0.01) ;
spots = new SpinSpots ( width /2 , height /2 , -0.02 , 90.0) ;
8 }

10 void draw () {
background (204) ;
12 arm . update () ;
arm . display () ;
14 spots . update () ;
spots . display () ;
16 }

18 class Spin {
float x , y , speed ;
20 float angle = 0.0;
Spin ( float xpos , float ypos , float s ) {
22 x = xpos ;
y = ypos ;
24 speed = s ;
}
26 void update () {
angle += speed ;
28 }
}
30

class SpinArm extends Spin {


32 SpinArm ( float x , float y , float s ) {
super (x , y , s ) ;

80
34 }
void display () {
36 strokeWeight (1) ;
stroke (0) ;
38 pushMatrix () ;
translate (x , y ) ;
40 angle += speed ;
rotate ( angle ) ;
42 line (0 , 0 , 165 , 0) ;
popMatrix () ;
44 }
}
46

class SpinSpots extends Spin {


48 float dim ;
SpinSpots ( float x , float y , float s , float d ) {
50 super (x , y , s ) ;
dim = d ;
52 }
void display () {
54 noStroke () ;
pushMatrix () ;
56 translate (x , y ) ;
angle += speed ;
58 rotate ( angle ) ;
ellipse ( - dim /2 , 0 , dim , dim ) ;
60 ellipse ( dim /2 , 0 , dim , dim ) ;
popMatrix () ;
62 }
}

1 #!/ usr / bin / ruby

3 class Being

5 @@count = 0

7 def initialize
@@count += 1
9 puts " Being class created "
end
11

def show_count

81
13 " There are #{ @@count } beings "
end
15

end
17

class Human < Being


19

def initialize
21 super
puts " Human is created "
23 end
end
25

class Animal < Being


27

def initialize
29 super
puts " Animal is created "
31 end
end
33

class Dog < Animal


35

def initialize
37 super
puts " Dog is created "
39 end
end
41

Human . new
43 d = Dog . new
puts d . show_count

Option Strict On
2

4 Module Example

6 Class Being
Sub New ()
8 Console . WriteLine ( " Being is created " )
End Sub
10 End Class

82
12 Class Human
Inherits Being
14

Sub New ()
16 Console . WriteLine ( " Human is created " )
End Sub
18

End Class
20

Sub Main ()
22

Dim h As New Human


24

End Sub
26

End Module

1 class Opt {
public :
3 // / Returns true if a barrier has been hit
virtual bool barrier ( double S ) =0;
5 // / Returns payoff at maturity for underlying price S
virtual double pay ( double S ) =0;
7 };

9 class Vanilla :
public Opt
11 {
// / Strike
13 double K ;
public :
15 bool barrier ( double S )
{
17 return false ;
}
19 double pay ( double S )
{
21 return (S - K ) >0 ? S - K : 0;
}
23 };

25 class NoBarrier :

83
public Opt
27 {
public :
29 bool barrier ( double S )
{
31 return false ;
}
33 };

35 class Vanilla2 :
public NoBarrier
37 {
// / Strike
39 double K ;
public :
41 double pay ( double S )
{
43 return (S - K ) >0 ? S - K : 0;
}
45 };

47 class PlainPay :
public Opt
49 {
// / Strike
51 double K ;
public :
53 double pay ( double S )
{
55 return (S - K ) >0 ? S - K : 0;
}
57 };

59 class Vanilla3 :
public NoBarrier ,
61 public PlainPay
{
63

};

7.11. Dibuje la jerarqua de clases en que se basa el siguiente codigo.


1 class Vehicle {

84
public :
3 explicit
Vehicle ( int topSpeed )
5 : m_topSpeed ( topSpeed )
{}
7 int TopSpeed () const {
return m_topSpeed ;
9 }

11 virtual void Save ( std :: ostream & ) const = 0;

13 private :
int m_topSpeed ;
15 };

17 class WheeledLandVehicle : public Vehicle {


public :
19 WheeledLandVehicle ( int topSpeed , int numberOfWheels
)
: Vehicle ( topSpeed ) , m_numberOfWheels (
numberOfWheels )
21 {}
int NumberOfWheels () const {
23 return m_numberOfWheels ;
}
25

void Save ( std :: ostream & ) const ; // is implicitly


virtual
27

private :
29 int m_numberOfWheels ;
};
31

class TrackedLandVehicle : public Vehicle {


33 public :
TrackedLandVehicle ( int topSpeed , int numberOfTracks
)
35 : Vehicle ( topSpeed ) , m_numberOfTracks (
numberOfTracks )
{}
37 int NumberOfTracks () const {
return m_numberOfTracks ;
39 }

85
void Save ( std :: ostream & ) const ; // is implicitly
virtual
41

private :
43 int m_numberOfTracks ;
};

class DrawableObject
2 {
public :
4 virtual void Draw ( GraphicalDrawingBoard &) const = 0;
// draw to GraphicalDrawingBoard
};
6

class Triangle : public DrawableObject


8 {
public :
10 void Draw ( GraphicalDrawingBoard &) const ; // draw a
triangle
};
12

class Rectangle : public DrawableObject


14 {
public :
16 void Draw ( GraphicalDrawingBoard &) const ; // draw a
rectangle
};
18

class Circle : public DrawableObject


20 {
public :
22 void Draw ( GraphicalDrawingBoard &) const ; // draw a
circle
};
24

typedef std :: list < DrawableObject * > DrawableList ;


26

DrawableList drawableList ;
28 GraphicalDrawingBoard drawingBoard ;

30 drawableList . pushback ( new Triangle () ) ;


drawableList . pushback ( new Rectangle () ) ;
32 drawableList . pushback ( new Circle () ) ;

86
34 for ( DrawableList :: const_iterator iter =
drawableList . begin () ,
endIter = drawableList . end () ;
36 iter != endIter ;
++ iter )
38 {
DrawableObject * object = * iter ;
40 object - > Draw ( drawingBoard ) ;
}

7.12. De estos dos programas, en cual estan los objetos mas acoplados?
1 public class CartEntry
{
3 public float Price ;
public int Quantity ;
5 }

7 public class CartContents


{
9 public CartEntry [] items ;
}
11

public class Order


13 {
private CartContents cart ;
15 private float salesTax ;

17 public Order ( CartContents cart , float salesTax )


{
19 this . cart = cart ;
this . salesTax = salesTax ;
21 }

23 public float OrderTotal ()


{
25 float cartTotal = 0;
for ( int i = 0; i < cart . items . Length ; i ++)
27 {
cartTotal += cart . items [ i ]. Price *
cart . items [ i ]. Quantity ;
29 }

87
cartTotal += cartTotal * salesTax ;
31 return cartTotal ;
}
33 }

1 public class CartEntry


{
3 public float Price ;
public int Quantity ;
5

public float GetLineItemTotal ()


7 {
return Price * Quantity ;
9 }
}
11

public class CartContents


13 {
public CartEntry [] items ;
15

public float GetCartItemsTotal ()


17 {
float cartTotal = 0;
19 foreach ( CartEntry item in items )
{
21 cartTotal += item . GetLineItemTotal () ;
}
23 return cartTotal ;
}
25 }

27 public class Order


{
29 private CartContents cart ;
private float salesTax ;
31

public Order ( CartContents cart , float salesTax )


33 {
this . cart = cart ;
35 this . salesTax = salesTax ;
}
37

public float OrderTotal ()

88
39 {
return cart . GetCartItemsTotal () * (1.0 f +
salesTax ) ;
41 }
}

7.13. Identifique en el siguiente codigo en C++ un problema con la herencia del miembro
meow.
class Felino {
2 public :
void meow () = 0;
4 };

6 class Gato : public Felino {


public :
8 void meow () { std :: cout << " miau \ n " ; }
};
10

class Tigre : public Felino {


12 public :
void meow () { std :: cout << " ROARRRRRR \ n " ; }
14 };

16 class Ocelote : public Felino {


public :
18 void meow () { std :: cout << " roarrrrr \ n " ; }
};

7.14. A partir del siguiente codigo Ruby hemos tratado de escribir un codigo Java con
la misma semantica, pero los resultados no son iguales. Cual es la diferencia y por
que? Como deberamos modificar el programa en Java para que haga lo mismo que
el programa en Ruby?
1 #!/ usr / bin / ruby

3 class Being

5 @@count = 0

7 def initialize
@@count += 1
9 puts " creamos un ser "
end

89
11

def show_count
13 " Hay #{ @@count } seres "
end
15

end
17

class Human < Being


19

def initialize
21 super
puts " creamos un humano "
23 end
end
25

class Animal < Being


27

def initialize
29 super
puts " creamos un animal "
31 end
end
33

class Dog < Animal


35

def initialize
37 super
puts " creamos un perro "
39 end
end
41

Human . new
43 d = Dog . new
puts d . show_count

class Being {
2

private int count = 0;


4

public Being () {
6 count ++;
System . out . println ( " creamos un ser " ) ;
8 }

90
10 public void getCount () {
System . out . format ( " hay %d seres %n " , count ) ;
12 }
}
14 class Human extends Being {

16 public Human () {
System . out . println ( " creamos un humano " ) ;
18 }
}
20 class Animal extends Being {

22 public Animal () {
System . out . println ( " creamos un animal " ) ;
24 }
}
26 class Dog extends Animal {

28 public Dog () {
System . out . println ( " creamos un perro " ) ;
30 }
}
32

public class Inheritance2 {


34

@SuppressWarnings ( " ResultOfObjectAllocationIgnored " )


36 public static void main ( String [] args ) {
new Human () ;
38 Dog dog = new Dog () ;
dog . getCount () ;
40 }
}

7.15. A partir de la siguiente template en C++, escriba una clase de C++ con la misma
semantica pero especfica para int.
1 template < class A_Type > class calc
{
3 public :
A_Type multiply ( A_Type x , A_Type y ) ;
5 A_Type add ( A_Type x , A_Type y ) ;
};

91
7 template < class A_Type > A_Type
calc < A_Type >:: multiply ( A_Type x , A_Type y )
{
9 return x * y ;
}
11 template < class A_Type > A_Type calc < A_Type >:: add ( A_Type
x , A_Type y )
{
13 return x + y ;
}

7.16. Los mixins son una construccion de Ruby que permite incorporar algunas de las
funcionalidades de la herencia multiple, ya que Ruby es un lenguaje con herencia
simple. Con un mixin se pueden incluir en una clase miembros de otra clase, con
la palabra clave include. Los name clashes, si los hay, se resuelven por el orden
de los include, de forma que la ultima clase anadida prevalece, y sus definiciones
son las que se imponen en el caso de conflicto. Teniendo esto en cuenta, describa el
comportamiento del siguiente pedazo de codigo.
module EmailReporter
2 def send_report
# Send an email
4 end
end
6

module PDFReporter
8 def send_report
# Write a PDF file
10 end
end
12

class Person
14 end

16 class Employee < Person


include EmailReporter
18 include PDFReporter
end
20

class Vehicle
22 end

24 class Car < Vehicle

92
include PDFReporter
26 include EmailReporter
end

7.17. Reescriba el siguiente codigo en Java en Haskell, de forma que su semantica denota-
cional sea identica.
1 import core . List ;
import static core . List . * ;
3

import java . util . NoSuchElementException ;


5

class P01 {
7

<T > T f1 ( List <T > list ) {


9 if ( list . isEmpty () ) throw new
NoSuchElementException ( " List is empty " ) ;
List <T > elements = list . tail () ;
11 List <T > result = list ;
while ( elements . nonEmpty () ) {
13 result = elements ;
elements = elements . tail () ;
15 }
return result . head () ;
17 }

7.18. El siguiente ejemplo en C++ causa un error de compilacion. Por que? como puede
solucionarse?
1 class trabajador
{
3 public :
void hora_de_levantarse ( )
5 { ... . ... ... . }
};
7 class estudiante
{
9 void hora_de_levantarse ( )
{ ... . ... ... . }
11 };
class ayudante_alumno : public trabajador , public
estudiante
13 {

93
15 };

17 int main ()
{
19 ayudante_alumno obj ;

21 obj . hora_de_levantarse ( )
}

7.19. Explique que tests realizara para saber cual es la visibilidad de un metodo protected
en Ruby.

7.20. Explique por que Smalltalk es menos eficiente que C++ utilizando el concepto de
overhead. Puede ayudarse de los siguientes graficos, en los que se muestra el proceso
de lookup de informacion asociada a los objetos en uno y otro lenguaje.

7.21. En el siguiente codigo el compilador esta creando una instancia de una subclase anoni-
ma de la clase abstracta, y luego se esta invocando al metodo de la clase abstracta.
Explique por que es necesario que el compilador haga este proceso.
abstract class my {
2 public void mymethod () {
System . out . print ( " Abstract " ) ;
4 }
}
6

94
class poly {
8 public static void main ( String a []) {
my m = new my () {};
10 m . mymethod () ;
}
12 }

7.22. En el siguiente programa en C++ indique si hay name clashes. Si los hay, iden-
tifquelos y mencione alguna poltica para solucionarlos. Si no los hay, introd
uzcalos
y a nada los mecanismos para solucionarlos (no es necesario que sea el mecanismo que
efectivamente implementa C++, puede ser otra poltica). En que contexto se dan
los name clashes?
class Persona
2 {
private :
4 std :: string m_strNombre ;
int m_nEdad ;
6 bool m_bEsVaron ;

8 public :
Persona ( std :: string strNombre , int nEdad , bool
bEsVaron )
10 : m_strNombre ( strNombre ) , m_nEdad ( nEdad ) ,
m_bEsVaron ( bEsVaron )
{
12 }

14 std :: string GetNombre () { return m_strNombre ; }


int GetEdad () { return m_nEdad ; }
16 bool EsVaron () { return m_bEsVaron ; }
};
18

class Empleado
20 {
private :
22 std :: string m_strEmpleador ;
double m_dSalario ;
24

public :
26 Empleado ( std :: string strEmpleador , double dSalario )
: m_strEmpleador ( strEmpleador ) ,
m_dSalario ( dSalario )

95
28 {
}
30

std :: string GetEmpleador () { return m_strEmpleador ; }


32 double GetSalario () { return m_dSalario ; }
};
34

class Profesor : public Persona , public Empleado


36 {
private :
38 int m_nDictaGrado ;

40 public :
Profesor ( std :: string strNombre , int nEdad , bool
bEsVaron ,
42 std :: string strEmpleador , double dSalario , int
nDictaGrado )
: Persona ( strNombre , nEdad , bEsVaron ) ,
44 Empleado ( strEmpleador , dSalario ) ,
m_nDictaGrado ( nDictaGrado )
46 {
}
48 };

7.23. Indique si el siguiente programa en C++ se podra escribir en Java con una semantica
equivalente. Si no se puede, indique por que. Si se puede, indique con que recursos
del lenguaje se puede.
class Persona
2 {
private :
4 std :: string m_strNombre ;
int m_nEdad ;
6 bool m_bEsVaron ;

8 public :
Persona ( std :: string strNombre , int nEdad , bool
bEsVaron )
10 : m_strNombre ( strNombre ) , m_nEdad ( nEdad ) ,
m_bEsVaron ( bEsVaron )
{
12 }

96
14 std :: string GetNombre () { return m_strNombre ; }
int GetEdad () { return m_nEdad ; }
16 bool EsVaron () { return m_bEsVaron ; }
};
18

class Empleado
20 {
private :
22 std :: string m_strEmpleador ;
double m_dSalario ;
24

public :
26 Empleado ( std :: string strEmpleador , double dSalario )
: m_strEmpleador ( strEmpleador ) ,
m_dSalario ( dSalario )
28 {
}
30

std :: string GetEmpleador () { return m_strEmpleador ; }


32 double GetSalario () { return m_dSalario ; }
};
34

class Profesor : public Persona , public Empleado


36 {
private :
38 int m_nDictaGrado ;

40 public :
Profesor ( std :: string strNombre , int nEdad , bool
bEsVaron ,
42 std :: string strEmpleador , double dSalario , int
nDictaGrado )
: Persona ( strNombre , nEdad , bEsVaron ) ,
44 Empleado ( strEmpleador , dSalario ) ,
m_nDictaGrado ( nDictaGrado )
46 {
}
48 };

7.24. En Java existen dos formas de crear un nuevo thread explcitamente: creando una
subclase de la clase Thread o bien implementando la interfaz Runnable. En ambos
casos el nuevo thread debe implementar el metodo run, que contendra el codigo
ejecutado por el thread. Cual de estas dos opciones crea un objeto mas versatil y por

97
que?

7.25. Estos dos programas, en dos lenguajes distintos, tienen la misma semantica. Cuales
son las diferencias entre estos dos lenguajes? Refierase a los elementos de los progra-
mas que ejemplifican las diferencias entre estos dos lenguajes. Cuando sea relevante,
argumente las ventajas y desventajas de la forma de expresion de cada lenguaje.
interface Iterable <T >
2 def each (& block : T - >)
end
4

interface Addable <T >


6 def +( other : T )
end
8

def sum ( values : Iterable <T >) where T : Addable <T >
10 count = 0
values . each do | value |
12 count += value
end
14 count
end

1 def sum ( values )


count = 0
3 values . each do | value |
count += value
5 end
count
7 end

98
Captulo 8

Tipos de datos

Mitchell 6.1., 6.2., 6.3., 6.4., 13.3

8.1. Ejercicios
Seleccione la(s) respuesta(s) correcta(s)
8.1. Los tipos son...

a) reglas sobre como usar las variables.


b) analizados por el compilador posteriormente al analisis sintactico.
c) conjuntos de valores.

8.2. el tipado estatico difiere del tipado dinamico en que...

a) en tipado estatico no puede haber coercion (casteo) de tipos.


b) en tipado dinamico tiene que haber coercion (casteo) de tipos.
c) el tipado estatico se hace en tiempo de compilacion, y el dinamico en tiempo de
ejecucion.
d) un lenguaje con tipado estatico no puede tener tipado dinamico.
e) en tipado estatico los tipos se tienen que declarar explcitamente, mientras que
en tipado dinamico no hay que declararlos.

8.3. La inferencia de tipos se realiza...

a) en tiempo de compilacion.
b) en tiempo de ejecucion.
c) parte en tiempo de compilacion, parte en tiempo de ejecucion.
d) con tipos explcitos.

99
8.4. Un lenguaje es seguro en tipado (type safe) si...

a) no se permite ning
un tipo de error atribuible a tipos.
b) se detectan como errores las discrepancias entre los tipos de variables y metodos
o funciones.

8.5. Los tipos built-in son...

a) los que provee el lenguaje propiamente.


b) el opuesto de los tipos user-defined.
c) todos los anteriores.
d) ninguno de los anteriores.

8.6. La diferencia entre polimorfismo y sobrecarga es...

a) que sobrecarga se aplica solo a algunos tipos, mientras que polimorfismo es mas
general.
b) que la sobrecarga se comprueba en tiempo de ejecucion y el polimorfismo en
tiempo de compilacion.
c) que en la sobrecarga tenemos diferentes implementaciones para un mismo smbo-
lo y en el polimorfismo tenemos una sola implementacion con tipos generales.
d) que la sobrecarga usa tipos concretos mientras que el polimorfismo usa variables
de tipo.
e) que la sobrecarga la analiza el compilador y el polimorfismo no.

Ejercicios pr
acticos
8.1. En base al siguiente arbol de parseo, calcule el tipo de datos de la funcion implemen-
tada en ML.

fun f(g,h) = g(h) + 2;

100
158 Type Systems and Type Inference
158 Type Systems and Type Inference



& !
&& !!
&& !!
&& !!
!!
&& !!
& & !
@
& & @
& & " !
& & "" !!
"" !!
&& !!
&& "" !!
@ "
&& @ !
2
& # 2
% ##
%%
%% ##
+% #
@
+ @#
int int int
int int int $ ##
$$ ##
$$ #
$ h
g h
g

6.6 Parse Graph


8.2.6.6EnParse Graph
Usebase al siguiente
the following arbol
parse graph de parseo,
to follow the steps calcule
of the MLel tipo de datos
type-inference algo- de la funci
on implemen-
rithmthe
Use
tada en following
ML.
on the parse
function graph to follow the steps of the ML type-inference algo-
declaration
rithm on the function declaration
fun f(g) = g(g) + 2;
fun f(g) = g(g) + 2;
fun
Whatf(g) = g(g)
is the output of the +
type2;
checker?
What is the output of the type checker?


& !
&& !!
&& !!
&& !!
& !!
& !!
& & !
@
& & @
&& " !
&& "" !!
"" !!
&& !!
&& "" !!
&& @" !
& @ 2
% # 2
%% # #
%% ##
+% #
@
+ @#
int int int
int int int $ ##
$$ ##
$ $ #
$ g
g g
g

8.3. Calcule el tipo de datos de la siguientes funciones implementadas en ML:

a) fun a(x,y) = x+2*y;


b) fun b(x,y) = x+y/2.0;

101
c) fun d(f,x) = f(f(x)));

8.4. Calcule el tipo de datos de las siguientes funciones en ML. Provea el arbol sintactico
de la funcion y aplique de forma explcita el algoritmo de inferencia de tipos, ya sea
sobre el arbol mismo o como sistema de ecuaciones.

fun a(x,y) = (x > 2) orelse (y < 10);

fun a(f,x,y) = f(x*2+3) andalso y;

fun d(f,x) = f(x*2) andalso true

fun aplicar(f,x) = f(x)

8.5. En el siguiente texto, explique si el dise


no de Crystal con duck typing mantiene
los principios de seguridad del tipado estatico, que errores se podran producir y
que errores no.

The language allows you to program using duck-typing, in a way. For exam-
ple if you have a variable duck.and you assign a Duck to it and invoke
quack.on it, given a Duck quacks, it will work. Now if later you assign a
Horse to it, and the horse doesnt quack, you will get a compile error. So,
the compiler catches the error at compile time. Now, if you assign another
type, say, a Goose, and it quacks, then the code continue to compile fine
(provided that it returns the correct type, which again is determined by its
methods, and so on). The new type will be Duck | Goose. In short: theres
no undefined method errors at runtime in Crystal.

102
Captulo 9

Programaci
on l
ogica

Mitchell 15.

9.1. Ejercicios
9.1. Por que el cut (!) no pertenece al paradigma declarativo?

a) porque no es un objetivo en prolog


b) porque tiene efectos secundarios

9.2. Cuales de los siguientes pares de terminos unifican? En el caso de que unifiquen,
indique como se instancian las variables.

a) pan = pan
b) Pan = pan
c) pan = pan
d) Pan = pan
e) pan = salchicha
f) comida(pan) = pan
g) comida(pan) = X
h) comida(X) = comida(pan)
i) comida(pan,X) = comida(Y,salchicha)
j) comida(pan,X,cerveza) = comida(Y,salchicha,X)
k) comida(pan,X,cerveza) = comida(Y,hamburguesa)
l) comida(X) = X
m) meal(comida(pan),bebida(cerveza)) = meal(X,Y)
n) meal(comida(pan),X) = meal(X,bebida(cerveza))

103
9.3. A partir de la siguiente base de datos:

elfo_domestico(dobby).
bruja(hermione).
bruja(McGonagall).
bruja(rita_skeeter).
puede_hacer_magia(X):- elfo_domestico(X).
puede_hacer_magia(X):- hechicero(X).
puede_hacer_magia(X):- bruja(X).

Cuales de las siguientes consultas se satisfacen? Con que instanciaciones de variables?

?- puede_hacer_magia(elfo_domestico).
?- hechicero(harry).
?- puede_hacer_magia(hechicero).
?- puede_hacer_magia(McGonagall).
?- puede_hacer_magia(Hermione).

Dibuje el arbol de b
usqueda para la consulta puede_hacer_magia(Hermione).
9.4. La busqueda en Prolog es exhaustiva, mientras que en otros lenguajes se ejecuta la
sentencia controlada por el primer patron que hace pattern matching. Sin embargo,
existe una forma en prolog para cortar una busqueda exhaustiva, usando el operador
cut, como se ve en el siguiente ejemplo:

max(X,Y,Y) :- X =< Y,!.


max(X,Y,X) :- X>Y.

Escriba un pseudocodigo en imperativo (usando if ... then ... else ...) o fun-
cional (usando guardas) con la misma semantica.
9.5. Defina en Prolog el problema de recomendar amigos en una red social, especificado
como sigue: para un usuario P con un conjunto de amigos CA = [A1 , A2 , ...An ],
los cuales a su vez tienen cada uno asociado un conjunto de amigos C1 , C2 ... Cn
le recomendaremos a P todos los miembros de la union de todos los conjuntos de
amigos de sus propios amigos CCA = C1 C2 ... Cn que NO estan en la lista de sus
propios amigos.
Pueden usar la siguiente base de conocimiento.

amigo(pedro,mar
a). amigo(pedro,juan).
amigo(mar
a,pedro). amigo(mar
a,clara).
amigo(mar
a,romina). amigo(juan,pedro).
amigo(juan,clara). amigo(clara,mar
a).
amigo(clara,juan). amigo(romina,mar
a).

104
9.6. Defina en Prolog un sistema de ayuda a una central de turnos. Este sistema dira si
se le puede asignar un turno a un paciente con un determinado medico para un
determinado da, siguiendo las siguientes premisas:

el paciente no tiene ning


un turno asignado para el mismo da a la misma hora.
el paciente no tiene ning
un turno asignado con ning
un especialista de la misma
especialidad para la que pide turno.
el medico para el que pide turno no tiene turno asignado con ning
un otro paciente
para el mismo da a la misma hora.

Se puede definir con una sola regla!


La base de conocimiento puede ser como sigue:

turno(celia,rivas,(6,30,8)).
turno(celia,zilvetti,(7,14,11)).
turno(tom
as,rivas,(7,11,10)).
turno(tom
as,p
erez,(8,11,10)).
turno(tom
as,schuster,(9,11,10)).
turno(lidia,zilvetti,(7,14,10)).
turno(lidia,schuster,(9,11,11)).
turno(esteban,rivas,(7,1,9)).

especialidad(rivas,oftalmologia).
especialidad(smith,oftalmologia).
especialidad(zilvetti,ginecologa).
especialidad(rom
an,ginecolog
a).
especialidad(p
erez,endocrinolog
a).
especialidad(schuster,cl
nico).

105
Captulo 10

Programaci
on concurrente

Mitchell 14.

10.1. Ejercicios
10.1. En el siguiente ejemplo se esta usando la variable comun lock como lock. Describa
una ejecucion del codigo que lleve a un estado inconsistente, describiendo en orden
las asignaciones y evaluaciones que ocurren al ejecutar el codigo. Explique como se
podra evitar este funcionamiento no deseado con implementaciones atomicas de wait
y signal.
1 lock := 0;
cobegin
3 begin
while lock =1 do end ;
5 lock :=1;
sign up ( fred ) ;
7 lock :=0;
end ;
9 begin
while lock =1 do end ; // loop que no hace nada hasta
que el lock sea 0
11 lock :=1; // setear el lock para entrar
a la seccion critica
sign up ( bill ) ; // seccion critica
13 lock := 0; // soltar el lock
end ;
15 end ;

10.2. En java existen dos formas de crear un nuevo thread explcitamente: creando una
subclase de la clase Thread o bien implementando la interfaz Runnable. En ambos

106
casos el nuevo thread debe implementar el metodo run, que contendra el codigo
ejecutado por el thread. Cual de estas dos opciones crea un objeto mas versatil y por
que?

10.3. El siguiente es un programa concurrente en Erlang. Escriba su version secuencial.


1 f1 (0) ->
0;
3 f1 (1) ->
1;
5 f1 ( N ) ->
Self = self () ,
7 spawn ( fun () ->
Self ! f0 (N -1)
9 end ) ,
spawn ( fun () ->
11 Self ! f0 (N -2)
end ) ,
13 receive
F1 ->
15 receive
F2 ->
17 F1 + F2
end
19 end .
}
21 }

10.4. Explique por que el esquema productor - consumidor no tiene condiciones de carrera.

10.5. En lenguajes con evaluacion perezosa la concurrencia sin condiciones de ca rrera


puede implementarse mediante estructuras de datos. Explique como, a partir del
siguiente ejemplo:
1 lista_dobles = map (\ x -> 2 * x ) lista_todos_los_naturales

10.6. Hay ocho combinaciones posibles en el pasaje de mensajes entre procesos concu-
rrentes, combinando sincronicidad / asincronicidad, ordenado / no ordenado y con
buffer / sin buffer. Explique cuales tienen ventajas y cuales no tienen sentido o tienen
desventajas.

10.7. Los actores se comunican mediante mensajes desordenados. Explique que informacion
y metodos debera a
nadir a un modelo basado en actores para poder procesar los
mensajes recibidos en el orden en el que fueron enviados.

107
10.8. Escriba en pseudocodigo dos versiones secuenciales (no concurrentes) del siguiente
programa, una en paradigma funcional y otra en paradigma imperativo. Argumente
las ventajas y desventajas de las tres versiones del programa usando los conceptos de
velocidad, overhead y determinismo.
1 class Ejemplo extends RecursiveTask < Integer > {
final int n ;
3 Ejemplo ( int n ) { this . n = n ; }
Integer compute () {
5 if ( n <= 1)
return n ;
7 Ejemplo f1 = new Ejemplo ( n - 1) ;
f1 . fork () ;
9 Ejemplo f2 = new Ejemplo ( n - 2) ;
return f2 . compute () + f1 . join () ;
11 }
}

10.9. En el codigo del ejercicio anterior, identifique las expresiones que expresan semantica
exclusiva de un programa concurrente y describa su semantica informalmente.

10.10. En las siguientes interfaces y clases de Java, subraye las partes de la descripcion que
describen semantica concurrente.
A Queue that additionally supports
operations that wait for the queue to
become non-empty when retrieving an
BlockingQueue<E>
element, and wait for space to become
available in the queue when storing an
element.
A Future represents the result of an
Future<E>
asynchronous computation.
An unbounded blocking queue that uses
the same ordering rules as class
PriorityBlockingQueue<E>
PriorityQueue and supplies blocking
retrieval operations.
A synchronization aid that allows a set
CyclicBarrier of threads to all wait for each other to
reach a common barrier point.
A blocking queue in which each insert
operation must wait for a corresponding
SynchronousQueue<E>
remove operation by another thread, and
vice versa.
10.11. En el siguiente codigo Java, subraye las partes del programa que hacen referencia a
semantica concurrente.

108
protected class ReadLock {
2 WriteLock lock ;

4 public ReadLock ( WriteLock lock ) {


super () ;
6 this . lock = lock ;
}
8

public void lock () throws InterruptedException { // If


write () then wait
10 synchronized ( lock ) {
System . out . println ( " Waiting to read :
" + Thread . currentThread () ) ;
12 Thread callingThread = Thread . currentThread () ;
while ( lock . isLocked && lock . lockedBy !=
callingThread ) {
14 lock . wait () ;
}
16 lock . readers ++; // Increment writelocks readers
System . out . println ( " Reading :
" + Thread . currentThread () ) ;
18 }
}
20

public void unlock () {


22 synchronized ( lock ) {
lock . readers - -; // Subtract from writelocks
readers
24 lock . notify () ;
}
26 }

28 }

10.12. En Java se puede escribir sincronizacion de metodos, pero esta funcionalidad es una
forma mas comoda de escribir (el llamado az
ucar sintactico), porque en realidad el al-
cance de una sincronizacion es siempre el objeto entero. Sabiendo esto, en el siguiente
codigo, pueden dos threads acceder los metodos addA() y addB() simultaneamente?
Por que? Podemos decir que las variables a y b son regiones crticas o canales de
comunicacion entre dos threads?
class X {
2

109
private int a ;
4 private int b ;

6 public synchronized void addA () {


a ++;
8 }

10 public synchronized void addB () {


b ++;
12 }

14 }

10.13. En Quake-C se usa un modelo de pasaje de mensajes. Aca mostramos algunos ti-
pos de mensaje, su especificacion y algunos ejemplos. Para los mensajes de tipo
MSG BROADCAST, MSG ONE, MSG ALL, diga cuales de las tres caractersticas
del pasaje de mensajes del modelo de actores tiene sentido usar: sincronizacion, buf-
fer, orden.
MSG_BROADCAST = 0; unreliable message , sent to all
2 MSG_ONE = 1; reliable message , sent to msg_entity
MSG_ALL = 2; reliable message , sent to all
4 MSG_INIT = 3; write to the init string
// Use unreliable ( but fast ) messages , when it s of no
importance that a client misses the message .
6 // examples : sound , explosions , monster deaths , taunts ... .
// Use reliable messages when it s very important that
every client sees the message , or a game incoherency
might happen .
8 // examples : shots , player deaths , door moves , game ends
... and CD track changes !.

10 Message : Killed Monster

12 WriteByte ( MSG_ALL , SVC_KILLEDMONSTER ) ;


Increase by one the count of killed monsters , as
available to the client . Can be displayed with
showscores .
14

Message : Set View Angles


16

msg_entity = player
18 WriteByte ( MSG_ONE , SVC_SETVIEWANGLES ) ;

110
WriteAngle ( MSG_ONE , camera . angles_x ) ;
20 WriteAngle ( MSG_ONE , camera . angles_y ) ;
WriteAngle ( MSG_ONE , camera . angles_z ) ;
22

Message : Final Message


24

WriteByte ( MSG_ALL , SVC_FINALE ) ;


26 WriteString ( MSG_ALL , " any text you like \ n " ) ;

111
Captulo 11

Paradigma de scripting

captulo 13. Michael Scott. Programming Language Pragmatics (2nd ed.). Morgan
Kaufmann. 2016.
Los lenguajes de programacion tradicionales estan disenados principalmente para crear
aplicaciones autonomas: programas que aceptan alg un tipo de entrada, la manipulan de al-
guna manera que se entiende bien, y generan la salida esperada. Pero la mayora de los usos
reales de las computadoras requieren la coordinacion de m ultiples programas. Un sistema
de nominas institucional, por ejemplo, debe procesar los datos de informes de los lectores
de tarjetas, formularios en papel escaneados, y la entrada manual (teclado); ejecutar miles
de consultas de bases de datos; cumplir cientos de normas jurdicas e institucionales; crear
un extenso rastro de papelpara el mantenimiento de registros, auditora, y los propositos
de preparacion de impuestos; cheques de impresion; y comunicarse con los servidores de
todo el mundo para el deposito directo on-line, la retencion de impuestos, la acumulacion
de jubilacion, seguro medico, y as sucesivamente. Estas tareas suelen involucrar docenas
o cientos de programas ejecutables por separado. La coordinacion entre estos programas
requiere pruebas y condicionales, bucles, variables y tipos, subrutinas y abstracciones: el
mismo tipo de herramientas logicas que un lenguaje convencional proporciona dentro de
una aplicacion.
En principio, cualquier lenguaje puede ser utilizado como un lenguaje de scripting,
siempre que cuente con las libreras o bindings para un entorno especfico. Sin embargo,
los lenguajes especficamente de scripting estan pensados para ser muy rapidos de apren-
der y escribir, ya sea como archivos ejecutables o de forma interactiva en un bucle de
lecturaevaluacionimpresion (REPL). En general, esto implica una sintaxis y semanti-
ca relativamente simples; tpicamente un scriptse ejecuta de principio a fin, como un
guion, sin un punto de entrada explcito.
Por ejemplo, no es com un utilizar Java como lenguaje de scripting debido a su sintaxis
verbosa y reglas sobre las clases que se encuentran en diferentes archivos, y directamente
es imposible ejecutar Java de forma interactiva, ya que los archivos fuente solo pueden
tener las definiciones que deben ser invocados externamente por una aplicacion lanzador
de aplicaciones. Por el contrario, en Python se pueden definir algunas funciones en un
solo archivo, evitar por completo las funciones y escribir en un estilo de programacion

112
imperativo, o incluso utilizarlo de forma interactiva.
Un lenguaje de scripting suele ser interpretado a partir del codigo fuente o el bytecode.
Los lenguajes de scripting pueden estar dise nados para ser usados por usuarios finales
de una aplicacion o solo para uso interno de los desarrolladores. Los lenguajes de scripting
suelen utilizar abstraccion para hacer transparente a los usuarios los detalles de los tipos
internos variables, almacenamiento de datos y la gestion de memoria.
Los scripts son a menudo creados o modificados por la persona que los ejecuta, pero a
menudo tambien se distribuyen, por ejemplo, buena parte de los juegos estan escritos en
un lenguaje de scripting.

11.1. Tipos de lenguajes de scripting


Podemos distinguir dos grandes familias de lenguajes de scripting: los de proposito
general y los especficos de dominio o de extension.
Los lenguajes de scripting de proposito general, como Perl y Python, se suelen llamar
lenguajes pegamento (glue languages), porque fueron dise nados originalmente para pe-
garlas salidas y entradas de otros programas, para construir sistemas mas grandes. Con
el crecimiento de la World Wide Web, los lenguajes de scripting han adquirido una nueva
importancia en la generacion de contenido dinamico.
Los lenguajes especficos de dominio estan pensados para extender las capacidades de
una aplicacion o entorno, ya que permiten al usuario personalizar o extender las funciona-
lidades mediante la automatizacion de secuencias de comandos.
Lenguajes como TCL o Lua se han dise nado especficamente como lenguajes de scripting
de proposito general, para ser incorporados en cualquier aplicacion. Otros lenguajes como
Visual Basic para Aplicaciones (VBA) esta pensado para facilitar la integracion en un
sistema operativo especfico.
Implementar las secuencias de comandos en estos lenguajes independientes de dominio
en lugar de desarrollar un nuevo lenguaje para cada aplicacion hace que los desarrolladores
no tengan que aprender nuevos lenguajes o traducir de un lenguaje a otro.
Si comparamos el paradigma de scripting con el resto, podemos pensarlo como compa-
rar secuencias de comandos con programacion del sistema. Los scripts son codigo bisagra,
su funcion es conectar componentes de software. Los lenguajes pegamento (glue langua-
ges) son los que se especializan para conectar componentes de software. Algunos ejemplos
clasicos son los pipes y las secuencias de comandos del shell. Tambien en el desarrollo web
se requiere esa funcionalidad, por ejemplo, para conectar un servidor web con diferentes
servicios web, como una base de datos.
Perl fue desarrollado para funcionar principalmente como lenguaje pegamento. Sin em-
bargo, pronto empezaron a expresarse partes mas grandes de la logica de los programas en
los scripts en perl, por lo que dejaron de cumplir su funcion de pegamento para pasar a
cumplir funcion de componentes de software en s, programas.
Los lenguajes pegamento son especialmente u tiles para escribir y mantener:
comandos personalizados para una consola de comandos;

113
programas mas peque
nos que los que estan mejor implementados en un lenguaje
compilado;

programas de contenedorpara archivos ejecutables, como manipular archivos y hacer


otras cosas con el sistema operativo antes o despues de ejecutar una aplicacion como
un procesador de textos, hoja de calculo, base de datos, ensamblador, compilador,
etc .;

secuencias de comandos que pueden cambiar;

prototipos rapidos de una solucion.

Los lenguajes de macros que manipulan componentes de un sistema o aplicacion es-


pecficos pueden funcionar tambien como lenguajes de pegamento. Otras herramientas
como AWK tambien pueden ser considerados lenguajes pegamento, al igual que cualquier
lenguaje implementado por un motor de Windows Script Host (VBScript, JScript y VBA
por defecto en Windows y motores de otros fabricantes, incluyendo implementaciones de
Rexx, Perl, Tcl, Python, XSLT, Ruby, Delphi, etc.). La mayora de las aplicaciones pueden
acceder y utilizar los componentes del sistema operativo a traves de los modelos de objetos
o de sus propias funciones.
Otros dispositivos, como las calculadoras programables, tambien pueden tener lenguajes
pegamento. Por ejemplo, la Texas Instruments TI-92, se puede programar de fabrica con
un lenguaje de secuencias de comandos. En la serie TI-Nspire de calculadoras se incluyeron,
ademas de las secuencias de comandos, el lenguaje Lua. Los lenguajes de alto nivel de la
mayora de las calculadoras graficas (frecuentemente variantes de Basic, a veces derivados
de Lisp, y, menos frecuentemente, derivados C), en muchos casos pueden pegar funciones,
tales como graficos de la calculadora, listas, matrices, etc. Para algunas de las maquinas de
TI y HP se usan conversores de C y Perl, REXX AWK, as como secuencias de comandos
shell a Perl, VBScript hacia y desde Perl. De esta forma se puede escribir un programa en
un lenguaje de pegamento para la eventual aplicacion (como un programa compilado) en
la calculadora.
Una clase importante de lenguajes de scripting ha crecido a partir de la automatizacion
del control de tareas, principalmente la puesta en marcha y control del comportamiento de
los programas del sistema. (En este sentido, se podra pensar que las shells son descendientes
del JCL de IBM, o Job Control Language, que se utilizaba para este proposito.) Muchos
de los interpretes de lenguajes doblan como interpretes de lnea de comandos, como el shell
de Unix o el MS-DOS COMMAND.COM.
Con el advenimiento de interfaces graficas de usuario, surgio un tipo especializado de
lenguaje de scripting. Estos lenguajes interact uan con los graficos de ventanas, men us,
botones, etc., al mismo nivel de abstraccion que resulta natural para un usuario humano.
Esto se hace facilitando que se puedan reproducir las acciones de un usuario, ya que estos
lenguajes se usan normalmente para automatizar las acciones del usuario. A estos lenguajes
tambien se les llama macrossi el control es a traves de la simulacion de las acciones de
presionar teclas o clics del raton, as como tocar o pulsar en una pantalla tactil activada.

114
Estos lenguajes podran, en principio, utilizarse para controlar cualquier aplicacion de
interfaz grafica de usuario; pero, en la practica, su uso es limitado porque estan muy ligados
a una aplicacion especfica o un sistema operativo particular. Hay algunas excepciones a
esta limitacion. Algunos lenguajes de scripting de GUI se basan en el reconocimiento de
objetos graficos a traves de sus pxeles de la pantalla de visualizacion. Estos lenguajes de
scripting de GUI no dependen del apoyo del sistema operativo o aplicacion.
Muchos programas de aplicacion grandes incluyen un peque no lenguaje de programa-
cion adaptado a las necesidades del usuario de la aplicacion. Del mismo modo, muchos
juegos de computadora utilizan un lenguaje de programacion especfico para expresar las
acciones de personajes no jugadores y del entorno del juego. Los lenguajes de este tipo estan
disenados para una sola aplicacion; y, si bien pueden parecerse superficialmente un lenguaje
especfico de proposito general (por ejemplo QuakeC se parece a C), tienen caractersticas
especficas que los distinguen, principalmente abstracciones que acortan la expresion de
los conceptos propios del juego. El Emacs Lisp, aunque un dialecto totalmente formada
y capaz de Lisp, contiene muchas caractersticas especiales que hacen que sea mas u til
para ampliar las funciones de edicion de Emacs. Un lenguaje de programacion especfico
de la aplicacion puede ser visto como un lenguaje de programacion especfico del dominio
especializado para una sola aplicacion.
Varios lenguajes han sido dise nados para reemplazar lenguajes de scripting especficos
una aplicacion, como estandares para tareas de scripting. De esta forma, los programadores
de aplicaciones (que trabajan en C o en otro lenguaje de programacion no de scripting)
incluye ganchoscon los que el lenguaje de scripting puede controlar la aplicacion. Estos
lenguajes pueden ser tecnicamente equivalentes a un lenguaje de extension especfico de
la aplicacion, pero cuando una aplicacion incorpora un lenguaje com un, el usuario puede
programar con el mismo lenguaje para diferentes aplicaciones. Una alternativa mas generi-
ca es simplemente proporcionar una librera que un lenguaje de proposito general puede
utilizar para controlar la aplicacion, sin modificar el lenguaje para el dominio especfico.
JavaScript comenzo como un lenguaje de secuencias de comandos en el interior de los
navegadores web, y esa sigue siendo su funcion principal. Sin embargo, la estandarizacion
del lenguaje como ECMAScript lo ha popularizado como lenguaje integrable de proposi-
to general. En particular, la aplicacion SpiderMonkey Mozilla esta incrustada en varios
ambientes.
TCL fue creado como un lenguaje de extension, pero ahora se usa principalmente como
un lenguaje de proposito general, parecido a Python, Perl o Ruby. Por otro lado, Rexx
fue creado originalmente como un lenguaje de control de trabajos, pero se usa como un
lenguaje de extension, as como un lenguaje de proposito general. Perl es un lenguaje de
proposito general, pero tena en 1990 el dialecto Oraperl que consiste en un binario de Perl
4 con Oracle Call Interface compilado. Sin embargo, desde entonces se ha sustituido por
una biblioteca (Perl Module), DBD :: Oracle.
Otras aplicaciones complejas y orientadas a tareas pueden incorporar y un lenguaje de
programacion para permitir a sus usuarios un mayor control y mas funcionalidades. Por
ejemplo, las herramientas de autora Autodesk Maya 3D incrustan el lenguaje de script
MEL, o Blender, que utiliza Python para desempe nar este papel.

115
11.2. Ejercicios
11.1. Si quiero un control fuerte de la seguridad de una aplicacion, debo renunciar a usar
un lenguaje de scripting como por ejemplo JavaScript? De por lo menos un argumen-
to que muestre las vulnerabilidades de un lenguaje de scripting y por lo menos dos
argumentos o estrategias con las que podra tratar de reducir los riesgos de un len-
guaje de scripting. Argumente que ventajas aportan las caractersticas del lenguaje
que implican vulnerabilidades.

11.2. En el siguiente codigo en Quake-C, identifique por lo menos 3 caractersticas que


lo identifican como un lenguaje de scripting, y argumente por que, si es posible,
comparandolo con C, lenguaje en el que esta basado Quake-C.
// Example of an alternate fiend jumping AI
2

// Here s a jumping algorithm I came up with for the


fiends in Quake .
4 // It s a pretty good monster cheat that has them jumping
for you and
// making it half the time no matter where you are . Even
from underwater .
6

// You can also change


8 // setsize ( self , VEC_HULL2_MIN , VEC_HULL2_MAX ) ;
// to
10 // setsize ( self , VEC_HULL_MIN , VEC_HULL_MAX ) ;
// in void () monster_demon1
12

// Quake only has three clipping hulls for moving


entities , point - size ,
14 // man - size , and shambler - size . Making the hull
man - sized gives the
// fiend a BIG edge in tracking you down , ( it s a
definite hack :)
16

DEMON . QC
18 ( Change demon1_run6 and demon1_jump4 )

20 void () demon1_jump1 ;
void () demon1_run6 =[ $run6 , demon1_run1 ]
22 {
ai_run (36) ;
24 // if underwater , give a chance to switch to jump AI
anyway

116
if ( self . waterlevel == 1)
26 {
if ( random () > 0.8 )
28 self . think = demon1_jump1 ;
}
30

};
32

void () demon1_jump4 =[ $leap4 , demon1_jump5 ]


34 {
local vector dir ;
36 local float dist ;

38 ai_face () ;

40 self . touch = Demon_JumpTouch ;


makevectors ( self . angles ) ;
42 self . origin_z = self . origin_z + 1;
if ( self . enemy . origin_z > self . origin_z + 40)
44 {
if ( infront ( self . enemy ) || self . waterlevel == 1 )
46 {
dir = normalize ( self . enemy . origin - self . origin ) ;
48 dist = vlen ( self . enemy . origin - self . origin ) ;
self . velocity = dir * dist ;
50 }
else
52 self . velocity = v_forward * 150;

54 dist = self . enemy . origin_z - self . origin_z ;

56 // a vertical velocity to jump just above player s


head
// for any given distance between z origins .
58 // ( hack numbers that work for Quake )
dist = dist - 237;
60 self . velocity_z = 714 + dist ;
}
62 else
{
64 // slow forward to fall off ledge if player down
// else normal horizontal velocity
66 if ( self . enemy . origin_z < self . origin_z - 40)

117
self . velocity = v_forward * 150;
68 else
self . velocity = v_forward * 600;
70

// check for wall in front


72 traceline ( self . origin - 0 0 16 ,
( self . origin - 0 0 16 ) +
v_forward * 150 , TRUE , self ) ;
74

// attempt to jump wall or normal vel .


76 if ( trace_fraction < 1)
{
78 self . velocity = v_forward * 110;
self . velocity_z = 250 * ( random () + 1) ;
80 }
else
82 self . velocity_z = 250;
}
84

if ( self . flags & FL_ONGROUND )


86 self . flags = self . flags - FL_ONGROUND ;
};
88

// ( Add random statement to CheckDemonJump () to avoid


hang - ups and
90 // enable going for it no matter what )

92 float () CheckDemonJump =
{
94 local vector dist ;
local float d ;
96

if ( random () < 0.4)


98 return TRUE ;

11.3. Ordene los siguientes fragmentos de codigo de mas de scripting a menos de scripting,
y explique cuales son los principios que han contribudo a su ordenamiento.
class HELLO_WORLD
2

creation
4 make
feature

118
6 make is
local
8 io : BASIC_IO
do
10 !! io
io . put_string ( " %N Hello World !!!! " )
12 end -- make

14 end -- class HELLO_WORLD

16 program hello_prog

18 root
HELLO_WORLD : " make "
20

22 cluster

24 " ./ "

26 end

28 include " library / lib . pdl "

30

end -- hello_prog

1 while 1 ,
disp ( hello world )
3 end

1 \ font \ HW = cmr10 scaled 3000


\ leftline {\ HW Hello World }
3 \ bye

1 PROC write . string ( CHAN output , VALUE string []) =


SEQ character . number = [1 FOR string [ BYTE 0]]
3 output ! string [ BYTE character . number ]

5 write . string ( terminal . screen , " Hello World ! " )

119
Captulo 12

Seguridad basada en lenguajes de


programaci
on

Mitchell 13.5

12.1. Ejercicios
12.1. En Java, hay varios mecanismos de seguridad a traves del lenguaje, y muchos de
ellos interact
uan. Los class loaders son parte del Java Runtime Environment (JRE)
y sirven para cargar dinamicamente, por demanda, clases en tiempo de ejecucion.
No se puede cargar ninguna clase sin un class loader. Sabiendo el rol del Security
Manager en Java, explique como imagina que debe ser la relacion entre el Security
Manager y un class loader para preservar seguridad.

12.2. Describa la intencion del Proof-Carrying-Code y como provee seguridad.

12.3. En lenguajes inseguros, se pueden implementar polticas de seguridad llamadas eje-


cucion defensiva. Describa algunas de estas polticas, por ejemplo, para pervenirse
de vulnerabilidades por debilidad en el sistema de tipos de un lenguaje de scripting.

12.4. La no interferencia es uno de los principios basicos de seguridad. Explique como los
niveles de visibilidad de un lenguaje orientado a objetos pueden contribuir a la no
interferencia. Ahora, explique como la visibilidad ortogonal (por ejemplo, el package
en Java) pueden contribuir a la implementacion de polticas de acceso a informacion
mas complejas que las basadas u nicamente en herencia.

12.5. Explique las vulnerabilidades asociadas a un sistema de tipos como el de un lenguaje


de scripting, que provee robustez y facilita casteos.

120
Ap
endice A

Ex
amenes de ediciones anteriores

121
Paradigmas de la Programacion Primer Parcial
23 de Abril de 2015

Apellido y Nombre:

1. [10 pt.] Seleccione todas las respuestas correctas entre las diferentes opciones que se dan para
completar cada oracion:

a) Una gramatica independiente de contexto (context-free grammar)...


1) tiene predicados.
2) es inambigua.
3) tiene reglas de reescritura.
4) describe la sintaxis de un lenguaje.
5) es mas expresiva que una gram atica dependiente del contexto.
6) es tan expresiva como una m aquina de Turing.
b) Las variables estan compuestas de... (elija la opci
on m
as precisa)
1) un nombre y un valor.
2) un l-valor y un r-valor.
3) un identificador y un valor.
4) un identificador que esta ligado a un valor que est
a ligado a una direcci
on en memoria.
5) un identificador que esta ligado a una direcci
on en memoria que contiene un valor.
6) una direccion de memoria, un valor y un identificador en el texto del programa.
c) La clausura de una funcion es necesaria para...
1) definir funciones en un alcance global
2) mantener la referencia a las variables locales de la funci
on si tenemos alcance din
amico
3) mantener la referencia a las variables locales de la funcion si tenemos alcance est
atico
4) mantener la referencia a las variables libres de la funci
on si tenemos alcance dinamico
5) mantener la referencia a las variables libres de la funci
on si tenemos alcance estatico
d) La diferencia entre polimorfismo y sobrecarga es...
1) que sobrecarga se aplica solo a algunos tipos, mientras que polimorfismo es m
as general.
2) que la sobrecarga se comprueba en tiempo de ejecuci on y el polimorfismo en tiempo de
compilacion.
3) que en la sobrecarga tenemos diferentes implementaciones para un mismo smbolo y en
el polimorfismo tenemos una sola implementaci on con tipos generales.
4) que la sobrecarga usa tipos concretos mientras que el polimorfismo usa variables de
tipo.
5) que la sobrecarga la analiza el compilador y el polimorfismo no.
e) Las excepciones se pueden usar para optimizar c
odigo...
1) porque tienen alcance dinamico.
2) porque son saltos explcitos entre partes del programa.
3) porque son imperativas.
4) porque son funcionales.

1
2. [20 pt.] Muestre con un ejemplo que la siguiente gram
atica es ambigua, y modifquela para que
se convierta en una gramatica inambigua.

<a> ::= <a> <b> | <b>


<b> ::= <hoja> | <c> | <a>
<c> ::= bin <d> <b> | bin <d> <b> ban <b>
<hoja> ::= a | b | c | d | e | f | g

3. [30 pt.] Diagrame una secuencia de pilas de ejecucion que representen los los diferentes momen-
tos de la ejecucion del siguiente programa, mostrando c
omo se apilan y desapilan los diferentes
activation records a medida que se va ejecutando el programa. Asuma que el lenguaje de pro-
gramacion tiene alcance estatico.
Nota de estilo: El nivel de granularidad de la descripci on puede limitarse a bloques del
texto del programa, no es necesario diagramar como estados distintos de la ejecuci on las
instrucciones que no apilen a otro bloque. Si lo desea, puede usar ndices en el mismo texto
del programa para una representaci on mas compacta de la pila.

var x=1;
function g(z) {return x+z;}
function f(y) {
var x = y*y;
return g(y);
}
f(2)

4. [20 pt.] En el siguiente programa tipo Algol,

begin
integer n;
procedure p(k: integer);
begin
k := k+3;
print(n);
n := n+(2*k);
end;
n := 0;
p(n);
print(n);
end;

Que dos valores se imprimen si k se pasa...

a) por valor?
b) por valor-resultado?
c) por referencia?

5. [20 pt.] Calcule el tipo de datos de la siguiente funci on en ML. Provea el arbol sint
actico de
la funci
on y aplique de forma explcita el algoritmo de inferencia de tipos, ya sea sobre el
arbol
mismo o como sistema de ecuaciones.

fun a(x,y) = x+2*y;

2
Paradigmas de la Programacion Recuperatorio del Primer Parcial
17 de Junio de 2015

Apellido y Nombre:

1. [10 pt.] Seleccione todas las respuestas correctas entre las diferentes opciones que se dan para
completar cada oracion:

a) Se puede expresar con una gram


atica libre de contexto la asignaci
on m
ultiple de variables?
1) No.
2) S, si se trata de de asignacion en secuencia, y no en espejo.
3) S, si se trata de un n
umero finito de variables.
4) Todas las anteriores.
5) Ninguna de las anteriores.
b) El siguiente codigo es codigo espagueti porque...
10 IF (X .GT. 1) GO TO 30
20 X = -X
IF (X .LT. 1) GO TO 60
30 IF (X*Y .LT. 1) GO TO 40
X = X-Y-Y
40 X = X+Y
60 CONTINUE
X = A
Y = B-A
GO TO 20
1) tiene bloques.
2) las lneas estan numeradas.
3) no podemos armar un arbol sint
actico del programa.
4) usa expresiones GOTO.
c) Todo activation record necesariamente tiene...
1) control link
2) access link
3) variables locales
4) todas las anteriores
5) ninguna de las anteriores
d) Los tipos son...
1) reglas sobre como usar las variables.
2) analizados por el compilador posteriormente al an
alisis sint
actico.
3) conjuntos de valores.
e) Se usa alcance dinamico en las excepciones...
1) para no colisionar con el alcance de las variables.
2) porque se busca encontrar un patr on recorriendo la pila de ejecuci
on.
3) porque el programador tiene m as claro que quiere que quien program o las libreras.

1
2. [20 pt.] Dada la siguiente gramatica:

<s> ::= skip


<s> ::= <x> = <v>
<s> ::= <s1> <s2>
<s> ::= local <x> in <s> end
<s> ::= if <x> then <s1> else <s2> end

A
nada las reglas de reescritura necesarias para que incluya las siguientes expresiones como parte
del lenguaje que describe la gramatica:

if ( B ) { skip } elsif ( A ) { skip }


B := 5

3. [20 pt.] Diagrame una secuencia de pilas de ejecuci


on que representen los diferentes momentos de
la ejecuci
on del siguiente programa, mostrando como se apilan y desapilan los diferentes activa-
tion records a medida que se va ejecutando el programa. Asuma que el lenguaje de programacion
tiene alcance dinamico.

var x=1;
function f1(z) {
f2(z - x);
}
function f2(z) {
if ( z <= 0 ) { return z; }
else { f1(z) }
}
f1(1)

4. [20 pt.] En el siguiente programa,

function fun(int a, int b, int c)


begin
a := b + c;
b := c + 1;
print a, b, c;
end
function main
begin
int i := 5;
int j := 10;
ink k := 15;
fun(i, j, j + k);
print i, j, k;
end

Que secuencias de valores se imprimen si el programa tiene pasaje de par


ametros...
a) por valor?
b) por referencia?
c) por valor-resultado?
5. [30 pt.] Calcule el tipo de datos de la siguiente funci
on en ML. Provea el
arbol sint
actico de
la funci
on y aplique de forma explcita el algoritmo de inferencia de tipos como sistema de
ecuaciones.

fun d(f,x) = f(x*2) andalso true

2
Paradigmas de la Programacion Segundo Parcial
11 de Junio de 2015

Apellido y Nombre:

Multiple Choice [10 pt.] Seleccione todas las respuestas correctas entre las diferentes opciones que
se dan para completar cada oracion:

1. La diferencia entre interfaces de Java y clases abstractas de C++ es que...


a) las clases abstractas de C++ se pueden usar para expresar herencia m ultiple en sus
descendientes, mientras que las interfaces de Java no.
b) las interfaces de Java no pueden heredar de ninguna clase, mientras que las clases
abstractas de C++ s.
c) las interfaces de Java se pueden usar para expresar herencia m
ultiple en sus descen-
dientes, mientras que las clases abstractas de C++ no.
d) las clases abstractas de C++ no pueden ser instanciadas, mientras que las interfaces
de Java s.
2. Tenemos un name clash cuando...
a) una clase hereda dos miembros con el mismo nombre de dos de sus madres.
b) las signaturas de dos clases cualquiera tienen un miembro en com
un.
3. Se
nale cuales de las siguientes expresiones son true en Prolog, es decir, en que casos se
encuentra una unificacion exitosa para la expresi
on:
a) Pan = pan
b) Pan = pan
c) comida(pan) = pan
d) comida(pan,X) = comida(Y,salchicha)
e) comida(pan,X,cerveza) = comida(Y,hamburguesa)
f) comida(X) = X
g) meal(comida(pan),bebida(cerveza)) = meal(X,Y)
h) meal(comida(pan),X) = meal(X,bebida(cerveza))

Programacion L ogica [20 pt.] La b usqueda en Prolog es exhaustiva, mientras que en otros len-
guajes se ejecuta la sentencia controlada por el primer patr
on que hace pattern matching. Sin
embargo, existe una forma en prolog para cortar una b usqueda exhaustiva, usando el operador
cut, que termina la busqueda cuando se encuentra el primer patr
on que hace pattern matching.
El siguiente fragmento de Prolog es un ejemplo del uso de cut. Escriba un pseudoc
odigo en
imperativo (usando if ... then ... else ...) o funcional (usando guardas) con la misma
semantica.

apostar(X) :- tenerdinero(X),!.
apostar(X) :- tomarprestado(X).

1
Herencia m ultiple [20 pt.] Cuando tenemos herencia m ultiple hay que resolver la herencia de
informacion conflictiva de las diferentes clases madre. Detecte en el siguiente c
odigo informacion
conflictiva heredada de las diferentes clases madre y especifique en lenguaje natural por lo menos
dos estrategias posibles para resolverla (por ejemplo, la estrategia de Ruby con mixins y la
estrategia de C++, pero tambien podra especificar una heurstica de su propia autora para
resolver el problema).
class DispositivoUSB
{
private :
long ID ;

public :
D i s p o s i t i v o U S B ( long lI D )
: ID ( lID )
{
}

long ObtenerID ( ) { return ID ; }


};

class DispositivoRed
{
private :
long ID ;

public :
D i s p o s i t i v o R e d ( long lI D )
: ID ( lID )
{
}

long ObtenerID ( ) { return ID ; }


};

c l a s s A d a p t a d o r W i r e l e s s : public DispositivoUSB , public D i s p o s i t i v o R e d


{
public :
A d a p t a d o r W i r e l e s s ( long lUSBID , long lNetworkID )
: D i s p o s i t i v o U S B ( lUSBID ) , D i s p o s i t i v o R e d ( lNetworkID )
{
}
};

int main ( )
{
A d a p t a d o r W i r e l e s s c54G ( 5 4 4 2 , 1 8 1 7 4 2 ) ;
c o u t << c54G . ObtenerID ( ) ;

return 0 ;
}

2
Acoplamiento [20 pt.] El grado de interdependencia de dos componentes de software se puede ex-
presar en terminos de acoplamiento. En las siguientes versiones del mismo programa, argumente
que versi
on es mas acoplada que la otra y por que.
class RepositorioClientes {

private r e a d o n l y BasededatosID b a s e d e d a t o s ;

public R e p o s i t o r i o C l i e n t e s ( BasededatosID b a s e d e d a t o s )

{ this . basededatos = basededatos ; }

public void A n i a d i r ( s t r i n g NombreCliente )

{ b a s e d e d a t o s . A n i a d i r F i l a ( C l i e n t e , NombreCliente ) ; }
}

i n t e r f a c e BasededatosID {

void A n i a d i r F i l a ( s t r i n g Tabla , s t r i n g Valor ) ;


}

c l a s s B a s e d e d a t o s : BasededatosID {

public void A n i a d i r F i l a ( s t r i n g Tabla , s t r i n g Valor ) {}


}

class RepositorioClientes {

private r e a d o n l y B a s e d e d a t o s b a s e d e d a t o s ;

public R e p o s i t o r i o C l i e n t e s ( B a s e d e d a t o s b a s e d e d a t o s )

{ this . basededatos = basededatos ; }

public void A n i a d i r ( s t r i n g NombreCliente )

{ b a s e d e d a t o s . A n i a d i r F i l a ( C l i e n t e , NombreCliente ) ; }
}

class Basededatos {

public void A n i a d i r F i l a ( s t r i n g Tabla , s t r i n g Valor ) {}


}

3
Concurrencia [20 pt.] Escriba en pseudoc odigo dos versiones secuenciales (no concurrentes) del
siguiente programa, una en paradigma funcional y otra en paradigma imperativo. Argumente
las ventajas y desventajas de las tres versiones del programa usando los conceptos de velocidad,
overhead y determinismo.
c l a s s Ejemplo extends RecursiveTask <I n t e g e r > {
f i n a l int n ;
Ejemplo ( int n ) { t h i s . n = n ; }
I n t e g e r compute ( ) {
i f ( n <= 1 )
return n ;
Ejemplo f 1 = new Ejemplo ( n 1 ) ;
f1 . fork ( ) ;
Ejemplo f 2 = new Ejemplo ( n 2 ) ;
return f 2 . compute ( ) + f 1 . j o i n ( ) ;
}
}

Visibilidad [10 pt.] En el siguiente codigo en Ruby, describa la visibilidad de la variable cuenta.
class Ser

@@cuenta = 0

def i n i t i a l i z e
@@cuenta += 1
p u t s creamos un s e r
end
def m u e s t r a c u e n t a
Hay #{@@cuenta} s e r e s
end
end
c l a s s Humano < S e r
def i n i t i a l i z e
super
p u t s creamos un humano
end
end
c l a s s Animal < S e r
def i n i t i a l i z e
super
p u t s creamos un animal
end
end
c l a s s Perro < Animal
def i n i t i a l i z e
super
p u t s creamos un p e r r o
end
end

Humano . new
d = Perro . new
puts d . muestra cuenta

4
Paradigmas de la Programacion Recuperatorio del Segundo Parcial
17 de Junio de 2015

Apellido y Nombre:

1. [10 pt.] Seleccione todas las respuestas correctas entre las diferentes opciones que se dan para
completar cada oracion:

a) La diferencia entre extends e implements en Java consiste en que...


1) extends crea relaciones de herencia, mientras que implements crea rela ciones de
implementacion.
2) extends se aplica para heredar de clases, mientras que implements se a plica para
heredar de interfaces.
3) extends solo permite herencia simple, mientras que implements permite herencia
multiple.
b) Muchos lenguajes proveen sintaxis amigable para referirse a algunas clases dentro de la
jerarqua de clases, que son:
1) las clases hermanas de la clase desde las que se las refiere.
2) la clase madre de la clase desde que se la refiere.
3) las clases ancestro de la clase desde la que se est
a trabajando.
4) la propia clase desde la que se est
a trabajando.
5) las clases descendientes de la clase desde la que se est
a trabajando.
c) Se
nale cuales de las siguientes expresiones son true en Prolog, es decir, en que casos se
encuentra una unificacion exitosa para la expresi
on:
1) Libro = libro
2) Revista = libro
3) Juan = leer(Juan)
4) convertir(booleano) = booleano
5) leer(odisea,Juan) = leer(Iliada,maria)
6) leer(odisea(juan),Maria) = leer(Juan,iliada(maria))
7) leer(odisea,Juan,iliada) = leer(Iliada,maria)
8) leer(odisea(juan),iliada(maria)) = leer(X,Y)

2. [20 pt.] Los actores se comunican mediante mensajes desordenados. Explique que informacion
y metodos debera anadir a un modelo basado en actores para poder procesar los mensajes
recibidos en el orden en el que fueron enviados.

1
3. [30 pt.] Reescriba el siguiente codigo en Java en un lenguaje declarativo, por ejemplo Haskell o
pseudocodigo declarativo, de forma que su sem antica denotacional sea identica.
c l a s s P01 {

<T> T f 1 ( L i s t <T> l i s t ) {
i f ( l i s t . isEmpty ( ) ) throw new NoHayElemento ( l i s t a v a c i a ) ;
L i s t <T> e l e m e n t s = l i s t . t a i l ( ) ;
L i s t <T> r e s u l t = l i s t ;
while ( e l e m e n t s . nonEmpty ( ) ) {
r e s u l t = elements ;
elements = elements . t a i l ( ) ;
}
return r e s u l t . head ( ) ;
}

4. [20 pt.] El siguiente ejemplo en C++ causa un error de compilaci


on. Por que? c
omo puede
solucionarse?
class trabajador
{
public :
void h o r a d e l e v a n t a r s e ( )
{ .... ... .... }
};
class estudiante
{
void h o r a d e l e v a n t a r s e ( )
{ .... ... .... }
};
c l a s s ayudante alumno : public t r a b a j a d o r , public e s t u d i a n t e
{

};

int main ( )
{
ayudante alumno o b j ;

obj . h o r a d e l e v a n t a r s e ( )
}

2
5. [20 pt.] En el siguiente codigo java, inserte en el main los mecanismos de manejo de excepciones
necesarios para capturar la excepcion de forma adecuada.
public c l a s s R e p a r t i r C o m e n s a l e s
{
public s t a t i c void main ( S t r i n g [ ] a r g s )
{
Mesa mesa = new Mesa ( 1 ) ;
System . out . p r i n t l n ( vamos a l l e n a r l a mesa 1 . . . ) ;
mesa . a n i a d i r C o m e n s a l ( Ana ) ;
mesa . a n i a d i r C o m e n s a l ( Juan ) ;
mesa . a n i a d i r C o m e n s a l ( Maria ) ;
mesa . a n i a d i r C o m e n s a l ( Pedro ) ;
mesa . a n i a d i r C o m e n s a l ( Juana ) ;
mesa . a n i a d i r C o m e n s a l ( Esteban ) ;
mesa . a n i a d i r C o m e n s a l ( L ola ) ;
}
}

public c l a s s Mesa
{
private int numeroDeComensales ;
private int numeroDeMesa ;
public Mesa ( int numeroDeMesa )
{
t h i s . numeroDeMesa = numeroDeMesa ;
}
public void a n i a d i r C o m e n s a l ( s t r i n g comensal ) throws ExcepcionMesaLlena
{
i f ( numeroDeComensales > 5 )
{
throw new ExcepcionMesaLlena ( numeroDeComensales )
}
else
{
numeroDeComensales += 1 ;
}
}
}

3
Paradigmas de la Programacion Examen Final
3 de Julio de 2015

Apellido y Nombre:

1. [10 pt.] Muestre con un ejemplo que la siguiente gramatica es ambigua, y modifquela para que
se convierta en una gramatica totalmente inambigua, sin ninguna ambig uedad.
<a> ::= <b> <c>
<a> ::= <b>
<b> ::= c d e
<b> ::= c d
<b> ::= c
<c> ::= d e
<c> ::= e

2. [10 pt.] Diagrame una secuencia de pilas de ejecucion que representen los los diferentes momen-
tos de la ejecucion del siguiente programa, mostrando c
omo se apilan y desapilan los diferentes
activation records a medida que se va ejecutando el programa. Asuma que el lenguaje de pro-
gramacion tiene alcance estatico.
Nota de estilo: El nivel de granularidad de la descripci on puede limitarse a bloques del
texto del programa, no es necesario diagramar como estados distintos de la ejecuci on las
instrucciones que no apilen a otro bloque. Si lo desea, puede usar ndices en el mismo texto
del programa para una representaci on mas compacta de la pila.

var x=1;
var y=5;
function g(z) {return x+z;}
function f(y) {
var x = y*y;
return g(y);
}
var x=5;
f(2)

3. [10 pt.] En el siguiente programa,

begin
integer n;
procedure p(k: integer);
begin
k := k+2;
print(n);
n := n+(2*k);
end;
n := 4;
p(n);
print(n);
end;

1
Que dos valores se imprimen si k se pasa...

a) por valor?
b) por valor-resultado?
c) por referencia?

4. [10 pt.] Calcule el tipo de datos de la siguiente funci on en ML. Provea el arbol sint
actico de
la funci
on y aplique de forma explcita el algoritmo de inferencia de tipos, ya sea sobre el
arbol
mismo o como sistema de ecuaciones.

fun a(x,y) = (x > 2) orelse (y < 10);

5. [10 pt.] En el siguiente codigo en Ruby, explique c omo se aproxima a la expresividad propia de
un lenguaje con herencia m ultiple, incluso si Ruby es un lenguaje que no tiene herencia multiple
sino simple. Compare este mecanismo con el de otro lenguaje que no tiene herencia m ultiple sino
simple pero que usa otro mecanismo para aproximarse a la expresividad de la herencia m ultiple:
Java con sus interfaces.
module EmailReporter
def s e n d r e p o r t
# Send an e m a i l
end
end

module PDFReporter
def s e n d r e p o r t
# Write a PDF f i l e
end
end

c l a s s Person
end

c l a s s Employee < Person


i n c l u d e EmailReporter
i n c l u d e PDFReporter
end

class Vehicle
end

c l a s s Car < V e h i c l e
i n c l u d e PDFReporter
i n c l u d e EmailReporter
end

6. [10 pt.] Senale cuales de las siguientes expresiones son true en Prolog, es decir, en que casos se
encuentra una unificacion exitosa para la expresi on, y c
omo se pueden instanciar los valores de
las variables en las unificaciones exitosas.

a) Verdadero = verdadero
b) Verdadero = verdadero
c) booleano(verdadero) = verdadero
d) booleano(verdadero,X) = booleano(Y,falso)

2
e) booleano(verdadero,X,falso) = booleano(Y,falso)
f) booleano(X) = X
g) bool(booleano(verdadero),booleano(falso)) = bool(X,Y)
h) bool(booleano(verdadero),X) = bool(X,booleano(falso))

7. [10 pt.] Dibuje la jerarqua de clases en que se basa el siguiente c


odigo y explique la funci
on de
la palabra clave virtual en este caso.
class A {
public :
explicit
A( i n t z )
: m z( z )
{}
int Z() const {
return m z ;
}

v i r t u a l v o i d Save ( s t d : : ostream& ) c o n s t = 0 ;

private :
int m z ;
};

c l a s s BA : p u b l i c A {
public :
BA( i n t z , i n t y )
: A( z ) , m y ( y )
{}
i n t Y( ) c o n s t {
return m y ;
}

v o i d Save ( s t d : : ostream& ) c o n s t ;

private :
int m y;
};

c l a s s CA : p u b l i c A {
public :
CA ( i n t z , i n t x )
: A( z ) , m x ( x )
{}
i n t X( ) c o n s t {
return m x ;
}
v o i d Save ( s t d : : ostream& ) c o n s t ;

private :
int m x;
};

3
8. [10 pt.] Escriba en pseudocodigo dos versiones secuenciales (no concurrentes) del siguiente
programa, una en paradigma funcional y otra en paradigma imperativo. Argumente las ventajas
y desventajas de las tres versiones del programa usando los conceptos de velocidad, overhead y
determinismo.
c l a s s Ejemplo extends RecursiveTask <I n t e g e r > {
f i n a l int n ;
Ejemplo ( int n ) { t h i s . n = n ; }
I n t e g e r compute ( ) {
i f ( n <= 1 )
return n ;
Ejemplo f 1 = new Ejemplo ( n 1 ) ;
f1 . fork ( ) ;
Ejemplo f 2 = new Ejemplo ( n 2 ) ;
return f 2 . compute ( ) + f 1 . j o i n ( ) ;
}
}

9. [20 pt.] En las siguientes funciones en ML:


e x c e p t i o n Excpt o f i n t ;
fun t w i c e ( f , x ) = f ( f ( x ) ) h a n d l e Excpt ( x ) => x ;
fun pred ( x ) = i f x = 0 then r a i s e Excpt ( x ) e l s e x 1;
fun dumb( x ) = r a i s e Excpt ( x ) ;
fun smart ( x ) = 1 + pred ( x ) h a n d l e Excpt ( x ) => 1 ;

Cu
al es el resultado de evaluar cada una de las siguientes expresiones?

a) twice(pred,1)
b) twice(dumb,1)
c) twice(smart,1)

Explique que excepcion se levanta en cada caso y d


onde se levanta. Ay
udese de los respectivos
diagramas de pilas de ejecucion

4
Ejercicios para libres
1. [-5 pt.] El siguiente es un ejemplo de spaghetti code. Reescrbalo en pseudoc
odigo de forma
que NO use saltos (GOTO), y en cambio use programaci on estructurada en bloques.
10 i = 0
20 i = i + 1
30 PRINT i ; s q u a r e d = ; i i
40 IF i >= 10 THEN GOTO 60
50 GOTO 20
60 PRINT Program Completed .
70 END

2. [-5 pt.] Si el resultado del siguiente programa es 19, que tipo de alcance tiene el lenguaje de
programacion en el que esta escrito, est
atico o din
amico?
val x = 4;
fun f ( y ) = xy ;
fun g ( x ) = l e t
f (3) + x ;
g(7);

3. [-5 pt.] Identifique en el siguiente codigo en C++ un problema con la herencia del miembro
meow.
class Felino {
public :
v o i d meow ( ) = 0 ;
};

c l a s s Gato : p u b l i c F e l i n o {
public :
v o i d meow ( ) { s t d : : c o u t << miau\n ; }
};

c l a s s Tigre : public Felino {


public :
v o i d meow ( ) { s t d : : c o u t << ROARRRRRR\n ; }
};

c l a s s Ocelote : public Felino {


public :
v o i d meow ( ) { s t d : : c o u t << r o a r r r r r \n ; }
};

5
4. [-5 pt.] En el siguiente codigo en Ruby, describa la visibilidad de la variable cuenta.
class Ser
@@cuenta = 0

def i n i t i a l i z e
@@cuenta += 1
p u t s creamos un s e r
end
def m u e s t r a c u e n t a
Hay #{@@cuenta} s e r e s
end
end
c l a s s Humano < S e r
def i n i t i a l i z e
super
p u t s creamos un humano
end
end
c l a s s Animal < S e r
def i n i t i a l i z e
super
p u t s creamos un animal
end
end
c l a s s Perro < Animal
def i n i t i a l i z e
super
p u t s creamos un p e r r o
end
end

Humano . new
d = Perro . new
puts d . muestra cuenta

6
Paradigmas de la Programacion Examen Final
6 de Agosto de 2015

Apellido y Nombre:

1. [10 pt.] Calcule el tipo de datos de la siguiente funci


on en ML. Provea el
arbol sint
actico de la
funci
on y aplique de forma explcita el algoritmo de inferencia de tipos.

fun aplicar(f,x) = f(x)

2. [10 pt.] En el siguiente programa,

begin
integer n;
procedure p(k: integer, i:integer);
begin
k := k+2;
i := k+i;
print(n,k);
n := n+(2*k);
end;
n := 4;
m := 4;
p(n,m);
print(n,m);
end;

Que valores se imprimen si el lenguaje tiene pasaje de par


ametros...

a) por valor?
b) por valor-resultado?
c) por referencia?

3. [10 pt.] Senale cuales de las siguientes expresiones son true en Prolog, es decir, en que casos se
encuentra una unificacion exitosa para la expresi on, y c
omo se pueden instanciar los valores de
las variables en las unificaciones exitosas.

a) pred(X,var) = pred(var,X)
b) bin(X,c(d,X)) = bin(2,c(d,Y))
c) pred(X,Y) = pred(P,P)
d) bin(X,c(d,X)) = bin(2,c(X,Y))
e) pred(foo,L) = pred(A1,A1)
f) pred(var) = var
g) bin(X,c(d,X)) = bin(X,c(d,Y))

1
4. [10 pt.] En el siguiente programa en C++ indique si hay name clashes. Si los hay, identifquelos y
mencione alguna poltica para solucionarlos. Si no los hay, introd
uzcalos y a
nada los mecanismos
para solucionarlos (no es necesario que sea el mecanismo que efectivamente implementa C++,
puede ser otra poltica). En que contexto se dan los name clashes?
c l a s s Persona
{
private :
s t d : : s t r i n g m strNombre ;
int m nEdad ;
bool m bEsVaron ;

public :
Persona ( s t d : : s t r i n g strNombre , int nEdad , bool bEsVaron )
: m strNombre ( strNombre ) , m nEdad ( nEdad ) , m bEsVaron ( bEsVaron )
{
}

s t d : : s t r i n g GetNombre ( ) { return m strNombre ; }


int GetEdad ( ) { return m nEdad ; }
bool EsVaron ( ) { return m bEsVaron ; }
};

c l a s s Empleado
{
private :
s t d : : s t r i n g m strEmpleador ;
double m d S a l a r i o ;

public :
Empleado ( s t d : : s t r i n g strEmpleador , double d S a l a r i o )
: m strEmpleador ( strEmpleador ) , m d S a l a r i o ( d S a l a r i o )
{
}

s t d : : s t r i n g GetEmpleador ( ) { return m strEmpleador ; }


double G e t S a l a r i o ( ) { return m d S a l a r i o ; }
};

c l a s s P r o f e s o r : public Persona , public Empleado


{
private :
int m nDictaGrado ;

public :
P r o f e s o r ( s t d : : s t r i n g strNombre , int nEdad , bool bEsVaron ,
s t d : : s t r i n g strEmpleador , double d S a l a r i o , int nDictaGrado )
: Persona ( strNombre , nEdad , bEsVaron ) ,
Empleado ( strEmpleador , d S a l a r i o ) ,
m nDictaGrado ( nDictaGrado )
{
}
};

2
5. [20 pt.] Explique por que se dice que las excepciones tienen alcance din amico. Use como ejemplo
el siguiente codigo, describiendo los valores que tiene la variable x en el momento de ser atrapada
(handle) y comparando esos valores con los valores que tendra la variable en el bloque de c odigo
donde se encuentra el handle si su alcance fuera est atico.
fun t w i c e ( f , x ) = f ( f ( x ) ) handle Excpt ( x ) => x ;
fun pred ( x ) = i f x = 0 then r a i s e Excpt ( x ) ; 5 ; e l s e x 1;
fun dumb( x ) = r a i s e Excpt ( x ) ; 3 ;
fun smart ( x ) = 1 + pred ( x ) ; 4 handle Excpt ( x ) => 1 ;

6. [10 pt.] En java existen dos formas de crear un nuevo thread explcitamente: creando una subclase
de la clase Thread o bien implementando la interfaz Runnable. En ambos casos el nuevo thread
debe implementar el metodo run, que contendr a el c
odigo ejecutado por el thread. Cu
al de estas
dos opciones crea un objeto mas versatil y por que?

7. [10 pt.] Diga si la sentencia

( ( 5 ) )

pertenece al lenguaje descripto por la siguiente gram


atica:
<s e x p > : : = <atomic symbol> | <n a t u r a l > | ( <s e x p > . <s e x p > )
<atomic symbol> : : = <l e t t e r > <atom part> | : <atom part>
<n a t u r a l > : : = {<number>}+
<atom part> : : = <empty> | <l e t t e r > <atom part> | <number> <atom part>
<l e t t e r > : : = a | b | . . . | z
<number> : : = 1 | 2 | . . . | 9
<empty> : : =

Si pertenece, muestre la secuencia de reglas que lo generan, si no pertenece, agregue las reglas
necesarias para que la gramatica pueda incluir esa sentencia en su lenguaje.

8. [10 pt.] Explique si el siguiente codigo da un error de compilaci


on y por que s o por que no.
class trabajador
{
public :
void h o r a d e l e v a n t a r s e ( )
{ .... ... .... }
};
class estudiante
{
void h o r a d e l e v a n t a r s e ( )
{ .... ... .... }
};
c l a s s ayudante alumno : public t r a b a j a d o r , public e s t u d i a n t e
{

};

int main ( )
{
ayudante alumno o b j ;
}

3
9. [10 pt.] Indique si el siguiente programa en C++ se podra escribir en Java con una sem antica
equivalente. Si no se puede, indique por que. Si se puede, indique con que recursos del lenguaje
se puede.
c l a s s Persona
{
private :
s t d : : s t r i n g m strNombre ;
int m nEdad ;
bool m bEsVaron ;

public :
Persona ( s t d : : s t r i n g strNombre , int nEdad , bool bEsVaron )
: m strNombre ( strNombre ) , m nEdad ( nEdad ) , m bEsVaron ( bEsVaron )
{
}

s t d : : s t r i n g GetNombre ( ) { return m strNombre ; }


int GetEdad ( ) { return m nEdad ; }
bool EsVaron ( ) { return m bEsVaron ; }
};

c l a s s Empleado
{
private :
s t d : : s t r i n g m strEmpleador ;
double m d S a l a r i o ;

public :
Empleado ( s t d : : s t r i n g strEmpleador , double d S a l a r i o )
: m strEmpleador ( strEmpleador ) , m d S a l a r i o ( d S a l a r i o )
{
}

s t d : : s t r i n g GetEmpleador ( ) { return m strEmpleador ; }


double G e t S a l a r i o ( ) { return m d S a l a r i o ; }
};

c l a s s P r o f e s o r : public Persona , public Empleado


{
private :
int m nDictaGrado ;

public :
P r o f e s o r ( s t d : : s t r i n g strNombre , int nEdad , bool bEsVaron ,
s t d : : s t r i n g strEmpleador , double d S a l a r i o , int nDictaGrado )
: Persona ( strNombre , nEdad , bEsVaron ) ,
Empleado ( strEmpleador , d S a l a r i o ) ,
m nDictaGrado ( nDictaGrado )
{
}
};

4
Ejercicios para libres
1. [-5 pt.] El siguiente es un ejemplo de spaghetti code. Reescrbalo en pseudoc
odigo de forma
que NO use saltos (GOTO), y en cambio use programaci on estructurada en bloques.
10 i = 0
20 i = i + 1
30 PRINT i ; s q u a r e d = ; i i
40 IF i >= 10 THEN GOTO 60
50 GOTO 20
60 PRINT Program Completed .
70 END

2. [-5 pt.] Si el resultado del siguiente programa es 19, que tipo de alcance tiene el lenguaje de
programacion en el que esta escrito, est
atico o din
amico?
val x = 4;
fun f ( y ) = xy ;
fun g ( x ) = l e t
f (3) + x ;
g(7);

3. [-5 pt.] Identifique en el siguiente codigo en C++ un problema con la herencia del miembro
meow.
class Felino {
public :
v o i d meow ( ) = 0 ;
};

c l a s s Gato : p u b l i c F e l i n o {
public :
v o i d meow ( ) { s t d : : c o u t << miau\n ; }
};

c l a s s Tigre : public Felino {


public :
v o i d meow ( ) { s t d : : c o u t << ROARRRRRR\n ; }
};

c l a s s Ocelote : public Felino {


public :
v o i d meow ( ) { s t d : : c o u t << r o a r r r r r \n ; }
};

5
4. [-5 pt.] En el siguiente codigo en Ruby, describa la visibilidad de la variable cuenta.
class Ser
@@cuenta = 0

def i n i t i a l i z e
@@cuenta += 1
p u t s creamos un s e r
end
def m u e s t r a c u e n t a
Hay #{@@cuenta} s e r e s
end
end
c l a s s Humano < S e r
def i n i t i a l i z e
super
p u t s creamos un humano
end
end
c l a s s Animal < S e r
def i n i t i a l i z e
super
p u t s creamos un animal
end
end
c l a s s Perro < Animal
def i n i t i a l i z e
super
p u t s creamos un p e r r o
end
end

Humano . new
d = Perro . new
puts d . muestra cuenta

6
Paradigmas de la Programacion Examen Final
3 de Julio de 2015

Apellido y Nombre:

1. [20 pt.] Estos dos programas, en dos lenguajes distintos, tienen la misma sem antica. Cu
ales son
las diferencias entre estos dos lenguajes? Refierase a los elementos de los programas que ejem-
plifican las diferencias entre estos dos lenguajes. Cuando sea relevante, argumente las ventajas
y desventajas de la forma de expresion de cada lenguaje.
i n t e r f a c e I t e r a b l e <T>
d e f each (& b l o c k : T >)
end

i n t e r f a c e Addable<T>
d e f +( o t h e r : T)
end

d e f sum ( v a l u e s : I t e r a b l e <T>) where T : Addable<T>


count = 0
v a l u e s . each do | v a l u e |
count += v a l u e
end
count
end

d e f sum ( v a l u e s )
count = 0
v a l u e s . each do | v a l u e |
count += v a l u e
end
count
end

2. [20 pt.] Describa las diferencias sem


anticas en el siguiente programa si se implementa en un
lenguaje con alcance estatico o con alcance din
amico. Si lo requiere, puede ayudarse de una
secuencia de pilas de ejecucion.
gcd 0 y = y
gcd x y = gcd ( y mod x ) x
myGCD x y | x < 0 = myGCD (x ) y
| y < 0 = myGCD x (y )
| y < x = gcd y x
| otherwise = gcd x y

1
3. [10 pt.] En el siguiente programa:

begin
integer a, b, c;
procedure p(x: integer, y: integer, z: integer);
begin
c := 5;
x := z;
c := 4;
y := z+a
end;
a := 2;
b := 4;
c := 1;
p(a, b, a+c);
print(b);
end;

Que dos valores se imprimen si k se pasa...

a) por valor?
b) por valor-resultado?
c) por referencia?

4. [10 pt.] Calcule el tipo de datos de la siguiente funci on en ML. Provea el arbol sint
actico de
la funci
on y aplique de forma explcita el algoritmo de inferencia de tipos, ya sea sobre el
arbol
mismo o como sistema de ecuaciones.

fun a(f,x,y) = f(x*2+3) andalso y

5. [10 pt.] Estos dos programas tienen una sem antica aproximadamente equivalente. Cual de
los dos es orientado a objetos? El que no es orientado a objetos, en que paradigma podra
clasificarse? Describa informalmente la sem
antica de estos programas. Describa muy brevemente
la estructura de cada uno de estos programas, marcando las diferencias entre ellos.
c l a s s Greeter
d e f i n i t i a l i z e ( name )
@name = name . c a p i t a l i z e
end

def salute
p u t s H e l l o #{@name } !
end
end

g = G r e e t e r . new ( world )
g . salute

H e l l o World . e a c h c h a r do | c h a r |
p r i n t char
end
p r i n t \n

2
6. [10 pt.] En el siguiente codigo, identifique las expresiones que expresan sem
antica exclusiva de
un programa concurrente y describa su sem antica informalmente.
c l a s s Ejemplo extends RecursiveTask <I n t e g e r > {
f i n a l int n ;
Ejemplo ( int n ) { t h i s . n = n ; }
I n t e g e r compute ( ) {
i f ( n <= 1 )
return n ;
Ejemplo f 1 = new Ejemplo ( n 1 ) ;
f1 . fork ( ) ;
Ejemplo f 2 = new Ejemplo ( n 2 ) ;
return f 2 . compute ( ) + f 1 . j o i n ( ) ;
}
}

7. [20 pt.] En las siguientes funciones en ML:


e x c e p t i o n Excpt o f i n t ;
fun t w i c e ( f , x ) = f ( f ( x ) ) h a n d l e Excpt ( x ) => x ;
fun pred ( x ) = i f x = 0 then r a i s e Excpt ( x ) e l s e x 1;
fun dumb( x ) = r a i s e Excpt ( x ) ;
fun smart ( x ) = 1 + pred ( x ) h a n d l e Excpt ( x ) => 1 ;

Cu
al es el resultado de evaluar cada una de las siguientes expresiones?

a) twice(pred,1)
b) twice(dumb,1)
c) twice(smart,1)

Explique que excepcion se levanta en cada caso y d


onde se levanta. Ay
udese de los respectivos
diagramas de pilas de ejecucion

3
Ejercicios para libres
1. [-5 pt.] El siguiente es un ejemplo de spaghetti code. Reescrbalo en pseudoc
odigo de forma
que NO use saltos (GOTO), y en cambio use programaci on estructurada en bloques.
10 i = 0
20 i = i + 1
30 PRINT i ; s q u a r e d = ; i i
40 IF i >= 10 THEN GOTO 60
50 GOTO 20
60 PRINT Program Completed .
70 END

2. [-5 pt.] Si el resultado del siguiente programa es 19, que tipo de alcance tiene el lenguaje de
programacion en el que esta escrito, est
atico o din
amico?
val x = 4;
fun f ( y ) = xy ;
fun g ( x ) = l e t
f (3) + x ;
g(7);

3. [-5 pt.] Identifique en el siguiente codigo en C++ un problema con la herencia del miembro
meow.
class Felino {
public :
v o i d meow ( ) = 0 ;
};

c l a s s Gato : p u b l i c F e l i n o {
public :
v o i d meow ( ) { s t d : : c o u t << miau\n ; }
};

c l a s s Tigre : public Felino {


public :
v o i d meow ( ) { s t d : : c o u t << ROARRRRRR\n ; }
};

c l a s s Ocelote : public Felino {


public :
v o i d meow ( ) { s t d : : c o u t << r o a r r r r r \n ; }
};

4
4. [-5 pt.] En el siguiente codigo en Ruby, describa la visibilidad de la variable cuenta.
class Ser
@@cuenta = 0

def i n i t i a l i z e
@@cuenta += 1
p u t s creamos un s e r
end
def m u e s t r a c u e n t a
Hay #{@@cuenta} s e r e s
end
end
c l a s s Humano < S e r
def i n i t i a l i z e
super
p u t s creamos un humano
end
end
c l a s s Animal < S e r
def i n i t i a l i z e
super
p u t s creamos un animal
end
end
c l a s s Perro < Animal
def i n i t i a l i z e
super
p u t s creamos un p e r r o
end
end

Humano . new
d = Perro . new
puts d . muestra cuenta

5
Paradigmas de la Programacion Examen Final
6 de Agosto de 2015

Apellido y Nombre:

1. [20 pt.] Escriba el siguiente programa excepciones en lugar del manejo de errores mediante
estructuras de control.
Nota: no es necesario que el programa compile, puede usar pseudoc
odigo siempre que su sem
antica sea
ambigua.
codigoErrorType r e a d F i l e {
i n i t i a l i z e codigoError = 0;

// a b r i r e l a r c h i v o ;
i f ( elArchivoEstaAbierto ) {
// d e t e r m i n a r l a l o n g i t u d d e l a r c h i v o ;
i f ( obtenemosLongitudArchivo ) {
// a l o j a r e s a c a n t i d a d de memoria ;
i f ( tenemosSuficienteMemoria ) {
// leemos e l a r c h i v o a memoria ;
if ( falloLectura ) {
c o d i g o E r r o r = 1;
}
} else {
c o d i g o E r r o r = 2;
}
} else {
c o d i g o E r r o r = 3;
}
// c e r r a r e l a r c h i v o ;
i f ( e l A r c h i v o N o S e C e r r o && c o d i g o E r r o r == 0 ) {
c o d i g o E r r o r = 4;
} else {
c o d i g o E r r o r = c o d i g o E r r o r and 4;
}
} else {
c o d i g o E r r o r = 5;
}
return c o d i g o E r r o r ;
}

2. [10 pt.] Calcule el tipo de datos de la siguiente funci


on en ML. Provea el
arbol sint
actico de la
funci
on y aplique de forma explcita el algoritmo de inferencia de tipos.

fun aplicar(f,x) = f(x)

3. [20 pt.] Escriba un programa (puede ser en pseudoc


odigo) que de diferentes resultados si se usa
pasaje de parametros por valor o por referencia.

1
4. [20 pt.] Indique si el siguiente programa en C++ se podra escribir en Java con una sem antica
equivalente. Si no se puede, indique por que. Si se puede, indique con que recursos del lenguaje
se puede.
c l a s s Persona
{
private :
s t d : : s t r i n g m strNombre ;
int m nEdad ;
bool m bEsVaron ;

public :
Persona ( s t d : : s t r i n g strNombre , int nEdad , bool bEsVaron )
: m strNombre ( strNombre ) , m nEdad ( nEdad ) , m bEsVaron ( bEsVaron )
{
}

s t d : : s t r i n g GetNombre ( ) { return m strNombre ; }


int GetEdad ( ) { return m nEdad ; }
bool EsVaron ( ) { return m bEsVaron ; }
};

c l a s s Empleado
{
private :
s t d : : s t r i n g m strEmpleador ;
double m d S a l a r i o ;

public :
Empleado ( s t d : : s t r i n g strEmpleador , double d S a l a r i o )
: m strEmpleador ( strEmpleador ) , m d S a l a r i o ( d S a l a r i o )
{
}

s t d : : s t r i n g GetEmpleador ( ) { return m strEmpleador ; }


double G e t S a l a r i o ( ) { return m d S a l a r i o ; }
};

c l a s s P r o f e s o r : public Persona , public Empleado


{
private :
int m nDictaGrado ;

public :
P r o f e s o r ( s t d : : s t r i n g strNombre , int nEdad , bool bEsVaron ,
s t d : : s t r i n g strEmpleador , double d S a l a r i o , int nDictaGrado )
: Persona ( strNombre , nEdad , bEsVaron ) ,
Empleado ( strEmpleador , d S a l a r i o ) ,
m nDictaGrado ( nDictaGrado )
{
}
};

2
5. [20 pt.] Diagrame la pila de ejecucion del siguiente programa en Bash, incluyendo la estructura
de control links y access links, asumiendo que el lenguaje de programaci
on tiene alcance est
atico.
x=1
f u n c t i o n g ( ) { echo $x ; x=2 ; }
f u n c t i o n f ( ) { l o c a l x=3 ; g ; }
f # d o e s t h i s p r i n t 1 , o r 3?
echo $x

Este programa esta escrito en Bash, que en la realidad tiene alcance din
amico. Justifique por
que puede ser que Bash se haya dise
nado con lenguaje dinamico.
Que imprimira este programa, ahora que sabemos que tiene alcance din
amico?

6. [10 pt.] Senale cuales de las siguientes expresiones son true en Prolog, es decir, en que casos se
encuentra una unificacion exitosa para la expresi on, y c
omo se pueden instanciar los valores de
las variables en las unificaciones exitosas.

a) f(X,Y) = f(P,P)
b) a(X,c(d,X)) = a(2,c(d,Y))
c) a(X,c(d,X)) = a(X,c(d,Y))
d) a(X,c(d,X)) = a(2,c(X,Y))
e) f(foo,L) = f(A1,A1)
f) f(a) = a
g) f(X,a) = f(a,X)

3
Ejercicios para libres
1. [-5 pt.] El siguiente es un ejemplo de spaghetti code. Reescrbalo en pseudoc
odigo de forma
que NO use saltos (GOTO), y en cambio use programaci on estructurada en bloques.
10 i = 0
20 i = i + 1
30 PRINT i ; s q u a r e d = ; i i
40 IF i >= 10 THEN GOTO 60
50 GOTO 20
60 PRINT Program Completed .
70 END

2. [-5 pt.] Si el resultado del siguiente programa es 19, que tipo de alcance tiene el lenguaje de
programacion en el que esta escrito, est
atico o din
amico?
val x = 4;
fun f ( y ) = xy ;
fun g ( x ) = l e t
f (3) + x ;
g(7);

3. [-5 pt.] Identifique en el siguiente codigo en C++ un problema con la herencia del miembro
meow.
class Felino {
public :
v o i d meow ( ) = 0 ;
};

c l a s s Gato : p u b l i c F e l i n o {
public :
v o i d meow ( ) { s t d : : c o u t << miau\n ; }
};

c l a s s Tigre : public Felino {


public :
v o i d meow ( ) { s t d : : c o u t << ROARRRRRR\n ; }
};

c l a s s Ocelote : public Felino {


public :
v o i d meow ( ) { s t d : : c o u t << r o a r r r r r \n ; }
};

4
4. [-5 pt.] En el siguiente codigo en Ruby, describa la visibilidad de la variable cuenta.
class Ser
@@cuenta = 0

def i n i t i a l i z e
@@cuenta += 1
p u t s creamos un s e r
end
def m u e s t r a c u e n t a
Hay #{@@cuenta} s e r e s
end
end
c l a s s Humano < S e r
def i n i t i a l i z e
super
p u t s creamos un humano
end
end
c l a s s Animal < S e r
def i n i t i a l i z e
super
p u t s creamos un animal
end
end
c l a s s Perro < Animal
def i n i t i a l i z e
super
p u t s creamos un p e r r o
end
end

Humano . new
d = Perro . new
puts d . muestra cuenta

5
Paradigmas de la Programacion Primer Parcial
21 de Abril de 2016

Apellido y Nombre:

1. [5 pt.] Seleccione todas las respuestas correctas entre las diferentes opciones que se dan para
completar cada oracion:

a) Una gramatica independiente de contexto (context-free grammar)...


1) tiene predicados.
2) es inambigua.
3) tiene reglas de reescritura.
4) describe la sintaxis de un lenguaje.
5) es mas expresiva que una gram atica dependiente del contexto.
6) es tan expresiva como una m aquina de Turing.
b) Las variables estan compuestas de... (elija la opci
on m
as precisa)
1) un nombre y un valor.
2) un l-valor y un r-valor.
3) un identificador y un valor.
4) un identificador que esta ligado a un valor que est
a ligado a una direcci
on en memoria.
5) un identificador que esta ligado a una direcci on en memoria que puede contener un
valor.
6) una direccion de memoria, un valor y un identificador en el texto del programa.
c) La clausura de una funcion es necesaria para...
1) definir funciones en un alcance global
2) mantener la referencia a las variables locales de la funci
on si tenemos alcance din
amico
3) mantener la referencia a las variables locales de la funcion si tenemos alcance est
atico
4) mantener la referencia a las variables libres de la funci
on si tenemos alcance dinamico
5) mantener la referencia a las variables libres de la funci
on si tenemos alcance estatico
d) La diferencia entre polimorfismo y sobrecarga es...
1) que sobrecarga se aplica solo a algunos tipos, mientras que polimorfismo es m
as general.
2) que la sobrecarga se comprueba en tiempo de ejecuci on y el polimorfismo en tiempo de
compilacion.
3) que en la sobrecarga tenemos diferentes implementaciones para un mismo smbolo y en
el polimorfismo tenemos una sola implementaci on con tipos generales.
4) que la sobrecarga usa tipos concretos y el polimorfismo usa variables de tipo.
5) que la sobrecarga la analiza el compilador y el polimorfismo no.
e) Las excepciones se pueden usar para optimizar c
odigo...
1) porque tienen alcance dinamico.
2) porque son saltos explcitos entre partes del programa.
3) porque son imperativas.
4) porque son funcionales.

1
2. [10 pt.] Muestre con un ejemplo que la siguiente gram
atica es ambigua, y modifquela para que
se convierta en una gramatica inambigua.
<s> ::= <i > | <o>
<i > ::= i <p> <s> | i <p> <s> e <s>
<p> ::= a | b | c | d
<o> ::= x | y | z | w

3. [10 pt.] En el siguiente programa, que valores retornar


an foo y bar si el alcance del lenguaje
es din
amico? y si es estatico?
const int b = 5;
int foo ()
{
int a = b + 5;
return a ;
}

i n t bar ( )
{
int b = 2;
return foo ( ) ;
}

i n t main ( )
{
foo ( ) ;
bar ( ) ;
return 0;
}

4. [20 pt.] En este fragmento de codigo se captura una excepci on y se vuelve a lanzar la misma
excepcion. Explique que sucede en este fragmento de c
odigo, ayud
andose de un diagrama de una
secuencia de estados de la pila de ejecucion, mostrando c
omo se apilan y desapilan los diferentes
activation records a medida que se va ejecutando el programa. Para mayor claridad, puede
acompa narlo de una descripcion verbal. Describa tambien verbalmente cual sera el objetivo de
este programa. Que sentido tiene capturar una excepci on para volver a lanzarla? Es eso lo
u
nico que se hace?
ITransaccion transaccion = null ;
try
{
t r a n s a c c i o n = s e s i o n . EmpiezaTransaccion ( ) ;
// h a c e r a l g o
t r a n s a c c i o n . Commit ( ) ;
}
catch
{
i f ( t r a n s a c c i o n != n u l l ) { t r a n s a c c i o n . R e s t a u r a r E s t a d o P r e v i o ( ) ; }
throw ;
}

2
5. [15 pt.] Calcule el tipo de datos de la siguiente funci on en ML. Provea el arbol sint
actico de
la funci
on y aplique de forma explcita el algoritmo de inferencia de tipos, ya sea sobre el
arbol
mismo o como sistema de ecuaciones.
fun f ( x , y , z ) = x && ( y o r z )

6. [20 pt.] El lenguaje de programacion Scala implementa como parte del lenguaje una heurstica
llamada linearizacion, que permite determinar que implementaci on se usar
a en un objeto que
hereda una componente con el mismo nombre de diferentes clases. Explique por que es necesario
que el lenguaje implemente esta heurstica (que problema resuelve) y de un ejemplo donde se
muestre una configuracion donde entrara en juego esta heurstica para evitar el problema, por
ejemplo, la configuracion conocida como el problema diamante.

7. [20 pt.] Java implementa un tipo de pasaje de par ametros que consiste en pasar por valor la
referencia a un objeto. Mediante este tipo de pasaje de par ametros, Se puede implementar la
funci
on swap para que intercambie el objeto A por el objeto B? Se puede implementar la funcion
swap para intercambiar algun elemento, algun pedazo de software? Cu al?

3
Paradigmas de la Programacion Segundo Parcial
14 de Junio de 2016

Apellido y Nombre:

1. [20 pt.] En el siguiente programa, identifique las porciones del programa que no son
funcionales, y mencione por que no lo son. Escriba en pseudocodigo un programa funcional
con la misma semantica que este programa.
Module Modulo1
Dim t e x t o As S t r i n g = textoUno

P u b l i c Sub ConcatenaConGuion ( ByVal textoAConcatenar As S t r i n g )


t e x t o = t e x t o & & textoAConcatenar
End Sub

Sub Main ( )
ConcatenaConGuion ( textoDos )
End Sub
End Module

2. [10 pt.] En el siguiente programa en Rust, identifique porciones del programa que tienen
semantica puramente concurrente. Las porciones del programa que ha se nalado, son
construcciones ling
usticas para forzar atomicidad en porciones del codigo?
f n main ( ) {
l e t greeting = Hello ;

l e t mut t h r e a d s = Vec : : new ( ) ;

f o r num i n 0 . . 1 0 {
t h r e a d s . push ( t h r e a d : : spawn ( move | | {
p r i n t l n ! ( { } from t h r e a d number {} , g r e e t i n g , num ) ;
}));
}

f o r thread in threads {
t h r e a d . j o i n ( ) . unwrap ( ) ;
}
}

3. [10 pt.] Explique el rol del Security Manager en Java.

1
4. [10 pt.] Los actores (modelo de programacion de concurrencia funcional) pueden realizar
una accion que consiste en redefinirse a s mismos, es decir, cambiar el codigo que ejecutan.
Explique como esto se relaciona con el cambio de estado de un objeto en programacion
orientada a objetos, y por que los actores no recurren al concepto de estado que usan los
objetos.
5. [10 pt.] Dada la siguiente base de conocimiento en Prolog, explique como sera la sucesion
de objetivos que hay que satisfacer para contestar la consulta nieto(X,leoncio).
p r o g e n i t o r ( juan , a l b e r t o ) .
progenitor ( luis , alberto ) .
progenitor ( alberto , leoncio ) .
p r o g e n i t o r ( geronimo , l e o n c i o ) .
p r o g e n i t o r ( l u i s a , geronimo ) .

hermano (A, B) :
p r o g e n i t o r (A, P) ,
p r o g e n i t o r (B, P) ,
A \== B .

n i e t o (A, B) :
p r o g e n i t o r (A, P) ,
p r o g e n i t o r (P , B ) .

6. [10 pt.] En lenguajes inseguros, se pueden implementar polticas de seguridad llamadas


ejecucion defensiva o programacion defensiva. Describa algunas de estas polticas,
por ejemplo, para pervenirse de vulnerabilidades por debilidad en el sistema de tipos de
un lenguaje de scripting.
7. [20 pt.] A partir del siguiente programa en pseudocodigo, diagrame los diferentes mo-
mentos por los que pasa la pila de ejecucion, y senale en esos momentos que variables
pueden ser recolectadas por el recolector de basura (garbage collector). En los activation
records solo necesita diagramar las variables locales. Asuma que todas las variables se
representan mediante punteros en los activation records, y que el valor de la variable se
encuentra en el heap, por lo tanto necesitan ser recolectadas por el recolector de basura.
Explique si la memoria asociada a alguna variable podra haber sido liberada antes de
que el recolector de basura la libere, por ejemplo, manualmente.
int bla
{ int bli = ...
i n t blu = ...
int blo = ...
{
int ble = bli bli
}
bli = . . .
{
int ble = blo + blo
}
}

8. [10 pt.] Ordene los siguientes fragmentos de codigo de mas de scripting a menos de
scripting, y explique cuales son los principios que han contribudo a su ordenamiento.
PROGRAM HELLO
DO 1 0 , I =1 ,10
PRINT , H e l l o World
10 CONTINUE
STOP
END

t e l l a p p l i c a t i o n S c r i p t a b l e Text E d i t o r
make new window
activate
s e t c o n t e n t s o f window 1 t o H e l l o World ! & r e t u r n
end t e l l

p r i n t H e l l o World !

import s t d . s t d i o ;
i n t main ( ) { w r i t e f l n ( H e l l o world ! ) ; r e t u r n 0 ; }
Paradigmas de la Programacion Recuperatorio del Primer Parcial
21 de Junio de 2016

Apellido y Nombre:

1. [20 pt.] En Perl las variables pueden tener alcance est atico (lexico) o din
amico, usando distintas
palabras clave en el momento de su asignaci on. En el siguiente programa se ve c omo las palabras
clave local y my tienen distintos efectos en la forma c omo las variables adquieren su valor.
Diga cual de estas dos palabras claves se usa para que el valor de la variable se asigne con alcance
est
atica y cual con alcance dinamico, y explique por que.
sub v i s i b l e {
print l a v a r i a b l e t i e n e e l v a l o r $var \n ;
}
sub uno {
l o c a l $var = v a l o r uno ;
visible ();
}
sub dos {
my $var = v a l o r dos ;
visible ();
}

$var = g l o b a l ;
visible (); # imprime g l o b a l
uno ( ) ; # imprime v a l o r uno
dos ( ) ; # imprime g l o b a l

2. [10 pt.] Diagrame una secuencia de pilas de ejecuci


on que representen los los diferentes momentos
de la ejecucion del siguiente programa, mostrando c omo se apilan y desapilan los diferentes
activation records a medida que se va ejecutando el programa.
l e t fun g ( y ) = y+3
fun h ( z ) = g ( g ( z ) )
in
h(3)
end ;

3. [10 pt.] Calcule el tipo de datos de la siguiente funci on en ML. Provea el arbol sint
actico de
la funci
on y aplique de forma explcita el algoritmo de inferencia de tipos, ya sea sobre el
arbol
mismo o como sistema de ecuaciones.
fun f ( x , y , z ) = x && ( y o r z )

4. [20 pt.] Java tiene una construccion ling ustica llamada try... catch... finally... cuya
sem antica consiste en que el bloque de c
odigo bajo el alcance de finally se ejecuta siempre, tanto
si se lanza una excepcion bajo el alcance de try como si no se lanzo. Si se lanza una excepci
on, la
excepci on funciona exactamente igual que si no existiera el bloque finally (aunque el bloque

1
finally se ejecuta en cualquier caso), si se puede capturar mediante el catch se captura, y si
no sigue buscando hasta que encuentra un catch que la pueda capturar. Sabiendo esto, explique
que se imprime si ejecutamos los siguientes tres programas y por que.
c l a s s Ejemplo1 {
public s t a t i c void main ( S t r i n g a r g s [ ] ) {
try {
System . out . p r i n t l n ( p r i m e r a s e n t e n c i a d e l b l o q u e t r y ) ;
int num=45/0;
System . out . p r i n t l n (num ) ;
}
catch ( ArrayIndexOutOfBoundsException e ) {
System . out . p r i n t l n ( ArrayIndexOutOfBoundsException ) ;
}
finally {
System . out . p r i n t l n ( b l o q u e f i n a l l y ) ;
}
System . out . p r i n t l n ( f u e r a d e l b l o q u e try catch f i n a l l y ) ;
}
}

c l a s s Ejemplo2 {
public s t a t i c void main ( S t r i n g a r g s [ ] ) {
try {
System . out . p r i n t l n ( p r i m e ra s e n t e n c i a d e l b l o q u e t r y ) ;
int num=45/0;
System . out . p r i n t l n (num ) ;
}
catch ( A r i t h m e t i c E x c e p t i o n e ) {
System . out . p r i n t l n ( A r i t h m e t i c E x c e p t i o n ) ;
}
finally {
System . out . p r i n t l n ( b l o q u e f i n a l l y ) ;
}
System . out . p r i n t l n ( f u e r a d e l b l o q u e try catch f i n a l l y ) ;
}
}

c l a s s Ejemplo3 {
public s t a t i c void main ( S t r i n g a r g s [ ] ) {
try {
System . out . p r i n t l n ( p r i m e r a s e n t e n c i a d e l b l o q u e t r y ) ;
int num=45/3;
System . out . p r i n t l n (num ) ;
}
catch ( ArrayIndexOutOfBoundsException e ) {
System . out . p r i n t l n ( ArrayIndexOutOfBoundsException ) ;
}
finally {
System . out . p r i n t l n ( b l o q u e f i n a l l y ) ;
}
System . out . p r i n t l n ( f u e r a d e l b l o q u e try catch f i n a l l y ) ;
}
}

2
5. [20 pt.] En muchos lenguajes de programaci on no se implementa herencia m
ultiple, sino que se
usan mecanismos alternativos. En este ejemplo de Scala, explique como se usa la construccion
trait de una forma parecida a las interfaces de Java o los mixins de Ruby para conseguir una
expresividad parecida a la herencia m
ultiple de C++.
t r a i t Cantante {
def c a n t a r { p r i n t l n ( cantando . . . ) }
//mas metodos
}

c l a s s Persona {
def e x p l i c a r { p r i n t l n ( humano ) }
//mas metodos
}

c l a s s Actor extends Persona with Cantante


c l a s s Actor extends Cantante with P e r f o r m e r

6. [20 pt.] En un lenguaje con pasaje por referencia, que sentencias tenemos que incorporar para
que una funcion de los mismos resultados que obtendramos si el pasaje de par ametros fuera
por valor-resultado (call by value-result)? Por ejemplo, que sentencias deberamos a
nadir y c
omo
deberamos modificar la siguiente funcion:
p r o c e d u r e p ( var x , y : i n t e g e r ) ;

begin

...

x:=x+1;
y:=y+1

...

end ;

p(z , z )

3
Paradigmas de la Programacion Recuperatorio del Segundo Parcial
21 de Junio de 2016

Apellido y Nombre:

1. [20 pt.] Estos dos programas estan escritos en Perl. Cu


al de los dos es funcional? Identifique
en el programa no funcional las partes del codigo que no son funcionales y explique c omo se
expresa la misma semantica en la versi
on funcional para que sea funcional.
my @numbers = qw( 1 3 5 6 7 8 ) ;

my $sum = 0 ;
fo r my $num ( @numbers ) { $sum += $num ; }

say $sum ;

my @numbers = qw( 1 3 5 6 7 8 ) ;

sub r e d u c e (&@) {
my ( $code , $ f i r s t , @ r e s t ) = @ ;
return $ f i r s t unless @r e s t ;

return $code >(


$first ,
r e d u c e ( $code , @ re st ) ,
);
}

sub sum {
return r e d u c e { $ [ 0 ] + $ [ 1 ] } @ ;
}

say sum @numbers ; $

2. [10 pt.] En el siguiente programa en Elixir, identifique porciones del programa que tienen
semantica puramente concurrente. Las porciones del programa que ha se
nalado, son construc-
ciones ling
usticas para forzar atomicidad en porciones del c
odigo?
t a s k = Task . async f n > p e r f o r m c o m p l e x a c t i o n ( ) end
other time consuming action ()
Task . a w a i t t a s k

3. [20 pt.] A partir del siguiente programa en pseudoc odigo, diagrame los diferentes momentos por
los que pasa la pila de ejecucion, y se
nale en esos momentos que variables pueden ser recolectadas
por el recolector de basura (garbage collector). En los activation records s olo necesita diagramar
las variables locales. Asuma que todas las variables se representan mediante punteros en los
activation records, y que el valor de la variable se encuentra en el heap, por lo tanto necesitan ser

1
recolectadas por el recolector de basura. Explique si la memoria asociada a alguna variable podra
haber sido liberada antes de que el recolector de basura la libere, por ejemplo, manualmente.
int bla = . . .
{ int bli = . . .
i n t blu = . . .
int blo = . . .
{
int ble = b l i bla
}
bli = . . .
{
int ble = b l i + blo
}
}

4. [10 pt.] El siguiente script en AppleScript busca en Computerworld.com el contenido del texto
que hay en el portapapeles. Identifique los elementos del programa que lo caracterizan dentro
del paradigma de scripting y explquelos.
s e t u r l 1 t o h t t p : / /www. computerworld . com/ a c t i o n / g o o g l e S e a r c h . do ? cx = 0 1 4 . . .
s e t u r l 2 t o &x=0&y=0&c o f=FORID %3A9#1214

t e l l a p p l i c a t i o n System Events
s e t myquery t o t h e c l i p b o a r d
end t e l l

s e t t h e q u e r y t o SaR ( myquery , , +)

t e l l a p p l i c a t i o n Chrome
activate
t e l l a p p l i c a t i o n System Events
t e l l p r o c e s s Chrome
c l i c k menu item New Tab o f menu F i l e o f menu bar 1
end t e l l
end t e l l
s e t theURL t o u r l 1 & t h e q u e r y & u r l 2
s e t URL o f document 1 t o theURL
end t e l l

5. [10 pt.] Dada la siguiente base de conocimiento en Prolog, explique como sera la sucesi
on de
objetivos que hay que satisfacer para contestar la consulta ama(X,mia).
hombre ( v i n c e n t ) .
mujer ( mia ) .
hombre ( j u l e s ) .
hombre ( john ) .
h i j a ( mia , john ) .
h i j o ( j u l e s , john ) .
ama(X,Y): padre (X,Y ) .
padre (Y, Z): hombre (Y) , h i j o ( Z ,Y ) .
padre (Y, Z): hombre (Y) , h i j a ( Z ,Y ) .

6. [10 pt.] Explique como se usa la firma de c


odigo en los mecanismos de seguridad de Java.
7. [20 pt.] En Occam los programas se construyen con las siguientes primitivas ling
usticas:

2
SEQ : una secuencia de comandos que suceden en orden estricto.
PAR : una lista de comandos que se ejecutan sin especificar el orden.
CHAN canales de comunicacion definidos para que los procesos se comuniquen (lectura o es-
critura).

En el siguiente programa en Occam:


CHAN a
VAR x , y
PAR
SEQ
x := 1
a!x
SEQ
a ?y
y := y + 2

Usando este programa como marco de comparaci on, explique c


omo los actores implementan
concurrencia sin condiciones de carrera, explicando c
omo se implementara este programa en
programacion con actores.

3
Paradigmas de la Programacion Examen Final
7 de Julio de 2016

Apellido y Nombre:

1. [5 pt.] El siguiente es un ejemplo de spaghetti code. Reescrbalo en pseudocodigo de


forma que NO use saltos (GOTO), y en cambio use programacion estructurada en bloques.
10 i = 0
20 i = i + 1
30 PRINT i ; s q u a r e d = ; i i
40 IF i >= 10 THEN GOTO 60
50 GOTO 20
60 PRINT Program Completed .
70 END

2. [10 pt.] Muestre con un ejemplo que la siguiente gramatica es ambigua, y modifquela
para que se convierta en una gramatica totalmente inambigua.
<a> ::= <b> <c>
<b> ::= <a>
<b> ::= []
<c> ::= c d e
<c> ::= c d
<c> ::= e

3. [15 pt.] Diagrame una secuencia de pilas de ejecucion que representen los los diferentes
momentos de la ejecucion del siguiente programa, mostrando como se apilan y desapilan
los diferentes activation records a medida que se va ejecutando el programa. Asuma que
el lenguaje de programacion tiene alcance estatico. Diga que retorna f(2) si el lenguaje
tiene alcance estatico, y que retornara con alcance dinamico. Se
nale tambien los puntos
de la ejecucion en los que el recolector de basura puede recolectar variables y diga cuales
pueden ser recolectadas.
var x=1;
var y=5;
f u n c t i o n g ( z ) { r e t u r n x+z ; }
function f (y) {
var x = yy ;
return g(y ) ;
}
var x=5;
f (2)

1
4. [10 pt.] Escriba un programa (puede ser en pseudocodigo) en el que el resultado sea
distinto si el pasaje de parametros es por valor, por referencia o por valor-resultado.
Explique que comparte el pasaje de parametros por valor-resultado con el pasaje por
valor y que comparte con el pasaje por referencia.

5. [10 pt.] El enlace dinamico (o dynamic dispatch) es un mecanismo de la programacion


orientada a objetos por el cual se selecciona con que metodo o funcion se respondera a
un determinado mensaje en tiempo de ejecucion. Es necesario hacer esto en tiempo de
ejecucion porque es imposible saber en tiempo de compilacion cual sera el tipo del mensaje
recibido. En C++ el tipo de dispatch por defecto es estatico, y para que se pueda realizar
dynamic dispatch hay que declarar un metodo como virtual. Los metodos virtuales se
implementan mediante una estructura de datos que se llama vtable o virtual table,
que define la correspondencia entre mensajes y metodos para cada clase. Comente la
utilidad y coste del dynamic dispatch en terminos de overhead y flexibilidad. Establezca
un paralelismo con el polimorfismo de tipos mediante un ejemplo.

6. [10 pt.] Supongamos que un lenguaje de programacion usa el smbolo = como operador
de unificacion, que imprimira el siguiente programa?
var x ;
var y=1;
function f (a , b) {
i f ( a = b ) { print si }
e l s e { p r i n t no }
}
f (x , y)

7. [10 pt.] Este codigo en Erlang implementa las mismas funcionalidades que los canales de
Go mediante los procesos y mensajes de Erlang. Identifique porciones del programa que
tienen semantica puramente concurrente. Identifique aquellas que tienen funcionalidades
especficas para pasaje de mensajes, como en el modelo de actores.
defmodule GoChannel do
d e f make do
spawn(&GoChannel . l o o p / 0 )
end

d e f w r i t e ( channel , v a l ) do
send ( channel , { : w r i t e , v a l })
end

d e f r ead ( c h a n n e l ) do
send ( channel , { : read , s e l f })

r e c e i v e do
{ : read , channel , v a l } > v a l
end
end

d e f l o o p do
r e c e i v e do
{ : read , c a l l e r } > r e c e i v e do

2
{ : w r i t e , v a l } > send ( c a l l e r , { : read , s e l f , v a l } ) ; l o o p
end
end
end
end

8. [10 pt.] En las siguientes funciones en ML:


e x c e p t i o n Excpt o f i n t ;
fun t w i c e ( f , x ) = f ( f ( x ) ) ha ndle Excpt ( x ) => x ;
fun pred ( x ) = i f x = 0 then r a i s e Excpt ( x ) e l s e x 1;
fun dumb( x ) = r a i s e Excpt ( x ) ;
fun smart ( x ) = 1 + pred ( x ) ha ndle Excpt ( x ) => 1 ;
Cual es el resultado de evaluar cada una de las siguientes expresiones?
a) twice(pred,1)
b) twice(dumb,1)
c) twice(smart,1)
Explique que excepcion se levanta en cada caso y donde se levanta. Ay
udese de los res-
pectivos diagramas de pilas de ejecucion.
9. [5 pt.] En Perl las variables pueden tener alcance estatico (lexico) o dinamico, usando
distintas palabras clave en el momento de su asignacion. En el siguiente programa se ve
como las palabras clave local y my tienen distintos efectos en la forma como las
variables adquieren su valor. Diga cual de estas dos palabras claves se usa para que el
valor de la variable se asigne con alcance estatica y cual con alcance dinamico, y explique
por que.
sub v i s i b l e {
print l a v a r i a b l e t i e n e e l v a l o r $var \n ;
}
sub uno {
l o c a l $var = v a l o r uno ;
visible ();
}
sub dos {
my $var = v a l o r dos ;
visible ();
}

$var = g l o b a l ;
visible (); # imprime g l o b a l
uno ( ) ; # imprime v a l o r uno
dos ( ) ; # imprime g l o b a l

10. [15 pt.] Escriba un programa (puede ser en pseudocodigo) en el que una variable sea el
medio por el cual se propagan efectos secundarios mas alla del alcance de una funcion.
Relacione este fenomeno con los paradigmas funcional e imperativo, incluyendo en su
explicacion los conceptos de determinismo en las componentes de software. Explique como
este comportamiento que puede originar tantos comportamientos inesperados puede llegar
a utilizarse de forma ventajosa en un esquema productor-consumidor.

3
Ejercicios para libres
1. [-10 pt.] Java tiene una construccion ling
ustica llamada try... catch... finally...
cuya semantica consiste en que el bloque de codigo bajo el alcance de finally se ejecuta
siempre, tanto si se lanza una excepcion bajo el alcance de try como si no se lanzo. Si
se lanza una excepcion, la excepcion funciona exactamente igual que si no existiera el
bloque finally (aunque el bloque finally se ejecuta en cualquier caso), si se puede
capturar mediante el catch se captura, y si no sigue buscando hasta que encuentra un
catch que la pueda capturar. Sabiendo esto, explique que se imprime si ejecutamos los
siguientes tres programas y por que.
c l a s s Ejemplo1 {
public s t a t i c void main ( S t r i n g a r g s [ ] ) {
try {
System . out . p r i n t l n ( primera s e n t e n c i a d e l b l o q u e t r y ) ;
int num=45/0;
System . out . p r i n t l n (num ) ;
}
catch ( ArrayIndexOutOfBoundsException e ){
System . out . p r i n t l n ( ArrayIndexOutOfBoundsException ) ;
}
finally {
System . out . p r i n t l n ( b l o q u e f i n a l l y ) ;
}
System . out . p r i n t l n ( f u e r a d e l b l o q u e trycatchf i n a l l y ) ;
}
}

c l a s s Ejemplo2 {
public s t a t i c void main ( S t r i n g a r g s [ ] ) {
try {
System . out . p r i n t l n ( primera s e n t e n c i a d e l b l o q u e t r y ) ;
int num=45/0;
System . out . p r i n t l n (num ) ;
}
catch ( A r i t h m e t i c E x c e p t i o n e ){
System . out . p r i n t l n ( A r i t h m e t i c E x c e p t i o n ) ;
}
finally {
System . out . p r i n t l n ( b l o q u e f i n a l l y ) ;
}
System . out . p r i n t l n ( f u e r a d e l b l o q u e trycatchf i n a l l y ) ;
}
}

c l a s s Ejemplo3 {
public s t a t i c void main ( S t r i n g a r g s [ ] ) {
try {
System . out . p r i n t l n ( primera s e n t e n c i a d e l b l o q u e t r y ) ;
int num=45/3;
System . out . p r i n t l n (num ) ;
}

4
catch ( ArrayIndexOutOfBoundsException e ){
System . out . p r i n t l n ( ArrayIndexOutOfBoundsException ) ;
}
finally {
System . out . p r i n t l n ( b l o q u e f i n a l l y ) ;
}
System . out . p r i n t l n ( f u e r a d e l b l o q u e trycatchf i n a l l y ) ;
}
}

2. [-5 pt.] En el siguiente codigo en Ruby, describa la visibilidad de la variable cuenta.


c l a s s S er
@@cuenta = 0

def i n i t i a l i z e
@@cuenta += 1
puts creamos un s e r
end
def m u e s t r a c u e n t a
Hay #{@@cuenta} s e r e s
end
end
c l a s s Humano < S er
def i n i t i a l i z e
super
puts creamos un humano
end
end
c l a s s Animal < S er
def i n i t i a l i z e
super
puts creamos un animal
end
end
c l a s s Perro < Animal
def i n i t i a l i z e
super
puts creamos un p e r r o
end
end

Humano . new
d = Perro . new
puts d . m u e s t r a c u e n t a

5
3. [-5 pt.] Si el resultado del siguiente programa es 19, que tipo de alcance tiene el lenguaje
de programacion en el que esta escrito, estatico o dinamico?
val x = 4;
fun f ( y ) = xy ;
fun g ( x ) = l e t
f (3) + x ;
g(7);

4. [-5 pt.] Identifique en el siguiente codigo en C++ un problema con la herencia del miem-
bro meow.
cl ass Felino {
public :
v o i d meow ( ) = 0 ;
};

c l a s s Gato : p u b l i c F e l i n o {
public :
v o i d meow ( ) { s t d : : co ut << miau\n ; }
};

c l a s s Tigre : public Felino {


public :
v o i d meow ( ) { s t d : : co ut << ROARRRRRR\n ; }
};

c l a s s Ocelote : public Felino {


public :
v o i d meow ( ) { s t d : : co ut << r o a r r r r r \n ; }
};

6
Paradigmas de la Programacion Examen Final
19 de Diciembre de 2016

Apellido y Nombre:

1. [5 pt.] Calcule el tipo de datos de la siguiente funcion en ML. Provea el arbol sintactico
de la funcion y aplique de forma explcita el algoritmo de inferencia de tipos, ya sea sobre
el arbol mismo o como sistema de ecuaciones.
fun a ( x , y ) = ( x > 2 ) o r e l s e ( y < 1 0 )

2. [5 pt.] Muestre con un ejemplo que la siguiente gramatica es ambigua, y modifquela


para que se convierta en una gramatica totalmente inambigua.
<a> ::= <b> <c>
<b> ::= <a>
<b> ::= []
<c> ::= c d e

3. [20 pt.] Diagrame la pila de ejecucion del siguiente programa en Bash, incluyendo la
estructura de control links y access links, asumiendo que el lenguaje de programacion
tiene alcance estatico.
x=1
f u n c t i o n g ( ) { echo $x ; x=2 ; }
f u n c t i o n f ( ) { l o c a l x=3 ; g ; }
f # does t h i s p r i n t 1 , o r 3?
echo $x

4. [10 pt.] El programa anterior esta escrito en Bash, que en la realidad tiene alcance
dinamico. Justifique por que puede ser que Bash se haya dise
nado con lenguaje dinamico.
Que imprimira este programa, ahora que sabemos que tiene alcance dinamico?
5. [10 pt.] Si quiero un control fuerte de la seguridad de una aplicacion, debo renunciar a
usar un lenguaje de scripting como por ejemplo JavaScript? De por lo menos un argu-
mento que muestre las vulnerabilidades de un lenguaje de scripting y por lo menos dos
argumentos o estrategias con las que podra tratar de reducir los riesgos de un lenguaje de
scripting. Argumente que ventajas aportan las caractersticas del lenguaje que implican
vulnerabilidades.
6. [10 pt.] Explique por que se elimino la herencia m ultiple en lenguajes creados poste-
riormente a C++, y que caractersticas tienen los mecanismos alternativos. Ayudandose
del siguiente texto, ejemplifique su explicacion mediante las caractersticas especficas
de los mixins, y extrapole a las interfaces de Java (o a los traits de PHP o Scala
o a alguna caracterstica similar que usted conozca), notando semejanzas y diferencias.
Que problemas tratan de evitar estos mecanismos? Que capacidades expresivas tienen?

1
A mixin can basically be thought of as a set of code that can be added
to one or more classes to add additional capabilities without using in-
heritance. In Ruby, a mixin is code wrapped up in a module that a class
can include or extend [...] Modules can either be included or extended
depending on whether the methods will be added as instance methods or
class methods, respectively.

7. [10 pt.] Senale cuales de las siguientes expresiones son true en Prolog, es decir, en que ca-
sos se encuentra una unificacion exitosa para la expresion, y como se pueden instanciar
los valores de las variables en las unificaciones exitosas.

a) f(X,Y) = f(P,P)
b) a(X,c(d,X)) = a(2,c(d,Y))
c) a(X,c(d,X)) = a(X,c(d,Y))
d) a(X,c(d,X)) = a(2,c(X,Y))
e) f(foo,L) = f(A1,A1)
f) f(a) = a
g) f(X,a) = f(a,X)

8. [10 pt.] El enlace dinamico (o dynamic dispatch) es un mecanismo de la programacion


orientada a objetos por el cual se selecciona con que metodo o funcion se respondera a
un determinado mensaje en tiempo de ejecucion. Es necesario hacer esto en tiempo de
ejecucion porque es imposible saber en tiempo de compilacion cual sera el tipo del mensaje
recibido. En C++ el tipo de dispatch por defecto es estatico, y para que se pueda realizar
dynamic dispatch hay que declarar un metodo como virtual. Los metodos virtuales se
implementan mediante una estructura de datos que se llama vtable o virtual table,
que define la correspondencia entre mensajes y metodos para cada clase. Comente la
utilidad y coste del dynamic dispatch en terminos de overhead y flexibilidad. Establezca
un paralelismo con el polimorfismo de tipos mediante un ejemplo.

9. [10 pt.] La siguiente funcion de Python a nade un elemento a una lista dentro de un
diccionario de listas. Modifquelo usando excepciones para manejar un posible error de
llaves (KeyError) si la lista con el nombre no existe todava en el diccionario, en lugar
de comprobar por adelantado si ya existe. Incluya una clausula finally (que se ejecu-
tara independientemente de si se lanza una excepcion o no).
def a n i a d i r a l i s t a e n d i c c i o n a r i o ( d i c c i o n a r i o , n o m b r e l i s t a , elemento ) :
i f n o m b r e l i s t a in d i c c i o n a r i o :
l = diccionario [ nombrelista ]
print ( %s ya t i e n e %d e l e m e n t o s . % ( n o m b r e l i s t a , l e n ( l ) ) )
else :
diccionario [ nombrelista ] = [ ]
print ( Creamos %s . % n o m b r e l i s t a )

d i c c i o n a r i o [ n o m b r e l i s t a ] . append ( elemento )

print ( Aniadimos %s a %s . % ( elemento , n o m b r e l i s t a ) )

10. [10 pt.] Este codigo en Erlang implementa las mismas funcionalidades que los canales de
Go mediante los procesos y mensajes de Erlang. Identifique porciones del programa que

2
tienen semantica puramente concurrente. Identifique aquellas que tienen funcionalidades
especficas para pasaje de mensajes, como en el modelo de actores.
defmodule GoChannel do
d e f make do
spawn(&GoChannel . l o o p / 0 )
end

d e f w r i t e ( channel , v a l ) do
send ( channel , { : w r i t e , v a l })
end

d e f r ead ( c h a n n e l ) do
send ( channel , { : read , s e l f })

r e c e i v e do
{ : read , channel , v a l } > v a l
end
end

d e f l o o p do
r e c e i v e do
{ : read , c a l l e r } > r e c e i v e do
{ : w r i t e , v a l } > send ( c a l l e r , { : read , s e l f , v a l } ) ; l o o p
end
end
end
end

11. [10 pt.] En el siguiente codigo en Scheme:


( define incr ())
( define get ( ) )

( l e t ((n 0))
( s e t ! i n c r ( lambda ( i ) ( s e t ! n (+ n i ) ) ) )
( s e t ! g e t ( lambda ( ) n ) ) )

Las variables incr y get son globales, y la variable n se comparte entre ambas. Explique lo
que hace este programa usando los conceptos de efectos secundarios y variable compartida,
y explique utilidades y problemas del estado explcito en programacion.

Ejercicios para libres


1. [-10 pt.] Java tiene una construccion ling
ustica llamada try... catch... finally...
cuya semantica consiste en que el bloque de codigo bajo el alcance de finally se ejecuta
siempre, tanto si se lanza una excepcion bajo el alcance de try como si no se lanzo. Si
se lanza una excepcion, la excepcion funciona exactamente igual que si no existiera el
bloque finally (aunque el bloque finally se ejecuta en cualquier caso), si se puede
capturar mediante el catch se captura, y si no sigue buscando hasta que encuentra un
catch que la pueda capturar. Sabiendo esto, explique que se imprime si ejecutamos los
siguientes tres programas y por que.

3
c l a s s Ejemplo1 {
public s t a t i c void main ( S t r i n g a r g s [ ] ) {
try {
System . out . p r i n t l n ( primera s e n t e n c i a d e l b l o q u e t r y ) ;
int num=45/0;
System . out . p r i n t l n (num ) ;
}
catch ( ArrayIndexOutOfBoundsException e ){
System . out . p r i n t l n ( ArrayIndexOutOfBoundsException ) ;
}
finally {
System . out . p r i n t l n ( b l o q u e f i n a l l y ) ;
}
System . out . p r i n t l n ( f u e r a d e l b l o q u e trycatchf i n a l l y ) ;
}
}

c l a s s Ejemplo2 {
public s t a t i c void main ( S t r i n g a r g s [ ] ) {
try {
System . out . p r i n t l n ( primera s e n t e n c i a d e l b l o q u e t r y ) ;
int num=45/0;
System . out . p r i n t l n (num ) ;
}
catch ( A r i t h m e t i c E x c e p t i o n e ){
System . out . p r i n t l n ( A r i t h m e t i c E x c e p t i o n ) ;
}
finally {
System . out . p r i n t l n ( b l o q u e f i n a l l y ) ;
}
System . out . p r i n t l n ( f u e r a d e l b l o q u e trycatchf i n a l l y ) ;
}
}

c l a s s Ejemplo3 {
public s t a t i c void main ( S t r i n g a r g s [ ] ) {
try {
System . out . p r i n t l n ( primera s e n t e n c i a d e l b l o q u e t r y ) ;
int num=45/3;
System . out . p r i n t l n (num ) ;
}
catch ( ArrayIndexOutOfBoundsException e ){
System . out . p r i n t l n ( ArrayIndexOutOfBoundsException ) ;
}
finally {
System . out . p r i n t l n ( b l o q u e f i n a l l y ) ;
}
System . out . p r i n t l n ( f u e r a d e l b l o q u e trycatchf i n a l l y ) ;
}
}

4
2. [-5 pt.] Dada la siguiente base de conocimiento en Prolog, explique como sera la sucesion
de objetivos que hay que satisfacer para contestar la consulta primo(X,juan).
p r o g e n i t o r ( esteban , juan ) .
p r o g e n i t o r ( ana , juan ) .
p r o g e n i t o r ( esteban , t e r e s a ) .
p r o g e n i t o r ( ana , t e r e s a ) .
p r o g e n i t o r ( marisa , n o e l i a ) .
progenitor ( felix , noelia ) .
p r o g e n i t o r ( marisa , t r i s t a n ) .
progenitor ( victor , tristan ) .
hermano ( este ban , v i c t o r ) .
primo (X,Y): t i o (Z ,X) , p r o g e n i t o r (Z ,Y) .
t i o (X,Y): hermano (X, Z ) , p r o g e n i t o r (Z ,Y ) .

3. [-5 pt.] En el siguiente programa,


begin
integer n;
procedure p(k : i n t e g e r ) ;
begin
k := k+2;
print (n ) ;
n := n+(2k ) ;
end ;
n := 4 ;
p(n ) ;
print (n ) ;
end ;

Que dos valores se imprimen si k se pasa...

a) por valor?
b) por valor-resultado?
c) por referencia?

4. [5 pt.] El siguiente es un ejemplo de spaghetti code. Reescrbalo en pseudocodigo de


forma que NO use saltos (GOTO), y en cambio use programacion estructurada en bloques.
10 i = 0
20 i = i + 1
30 PRINT i ; s q u a r e d = ; i i
40 IF i >= 10 THEN GOTO 60
50 GOTO 20
60 PRINT Program Completed .
70 END

Você também pode gostar