Documente Academic
Documente Profesional
Documente Cultură
Teoría de Lenguajes
(Unidad II)
Tema:
Conceptos y Constructores
Dictado por:
DIANA CECILIA MUÑOZ CASANOVA
M.S. en Ingeniería de Sistemas e Informática
CHIMBOTE – PERÚ
2007
CAPÍTULO I: PROGRAMACIÓN CON ASIGNACIONES
1.1 Concepto. 62
1.2 Características 63
1.3 Operadores de asignación 63
1.4 Bloques de asignación 64
1.5 Efecto de una asignación 64
1.6 Asignaciones de valores de verdad. 66
1.7 Lenguajes que utilizan la programación en base a asignaciones. 66
Asignación en RUBY 66
Asignación en C++ 70
Asignación en JSCRIPT. 71
Asignación en PHP 72
2.1 Concepto 74
2.2 Declaración de procedimiento 76
2.3 Ventajas y desventajas de los procedimientos 77
2.4 Utilización de datos en los procedimientos 78
2.5 Métodos de pasos de parámetros 79
2.5.1. Invocación por valor 79
2.5.2. Invocación por referencia 79
2.6 Tiempo de vida animada de las activaciones 80
2.7 Estructura del bloque 80
2.7.1. Propósito de las estructuras de bloque 80
2.7.2. Procedimiento como parámetros 81
2.8 Disposición de tipo de dato asignable 81
2.9 Apuntadores y asignación dinámica de valores 82
2.9.1. Punteros 82
2.9.2. Variables estáticas 82
2.9.3. Variables dinámicas 83
2.9.4. Estructura de datos ligados. 83
2.9.5. Operaciones con punteros 83
4.1 Concepto 98
4.2 El objetivo 98
4.3 Características propias de estos lenguajes 98
4.4 Categorías de lenguajes funcionales 99
Universidad Nacional del Santa
Facultad de Ingeniería Manual de Teoría de lenguajes
E. A. P Ingeniería de Sistemas e Informática Unidad II
1.1 CONCEPTO.
1.2 CARACTERÍSTICAS
Los operadores de asignación, son aquellos que nos permiten modificar el valor de
una variable, el operador de asignación básico es el 'es igual a' (=), que da el valor
que lo sigue a la variable que lo precede:
Veamos un ejemplo de una instrucción tonta, que en realidad no hace nada.
algo = algo;
La variable algo toma el valor de algo; todo queda como antes. Ahora aumentemos
el valor de la variable en 3 unidades.
algo = algo + 3;
Aquí la variable toma el valor que tenía mas 3 unidades. Existe una forma de
simplificar la notación anterior. Es la siguiente:
algo += 3; // equivalente a algo = algo + 3
Donde:
<expresión 1 > denota localidad de memoria .
<expresión 2 > denota un valor.
read M[1]
read M[2] Entrada
M[1] := M[1]- M[2]
Control If M[1] >=then goto 3
M[1] := M[1] + M[2]
Write M[1] Salida
Halt
0 1 2 3
Memoria 27 10
En el caso 1. La primera lista son las variables que tienen asociado el valor
verdadero, y la segunda las variables que tienen asociado el valor falso. Puede
haber variables repetidas. Si una variable aparece en las dos listas se considera que
su valor es verdadero.
En el caso 2. Se utiliza un nombre simbólico para representar una asignación. Este
nombre debe haber sido definido mediante un comando asignar. Como los
presentados en la anterior tabla.
En el caso 3.La asignación coincide con A salvo en los valores especificados entre
corchetes. Si se repiten variables se toma el último valor (el que aparece más a la
derecha)
ASIGNACIÓN EN RUBY
Hay dos formas básicas de asignación en Ruby. La primera asigna una
referencia de objeto a una variable o constante. Esta forma de asignación esta
integrada en el lenguaje.
instrument = "piano"
MIDDLE_A = 440
aSong.duration = 234
instrument["ano"] = "ccolo"
No hay necesidad o razón para que estos métodos para establecer atributos
tengan que corresponder con variables de instancia internas, o que tenga que
existir un lector de atributos por cada modificador de atributos
class Amplifier
def volume=(newVolume)
self.leftChannel = self.rightChannel = newVolume
end
# ...
End
Asignación Paralela
Durante la primera semana en un curso de programación (ó el segundo
semestre si es una escuela oficial), tal vez tendría que escribir código para
intercambiar los valores en dos variables:
int a = 1;
int b = 2;
int temp;
temp = a;
a = b;
b = temp;
a = [1, 2, 3, 4]
b, c = a » b == 1, c == 2
b, *c = a » b == 1, c == [2, 3, 4]
b, c = 99, a » b == 99, c == [1, 2, 3, 4]
b, *c = 99, a » b == 99, c == [[1, 2, 3, 4]]
b, c = 99, *a » b == 99, c == 1
b, *c = 99, *a » b == 99, c == [1, 2, 3, 4]
Asignaciones Anidadas
Las asignaciones paralelas tienen otra característica que vale la pena
mencionar. El lado izquierdo de la asignación puede contener una lista de
términos dentro de paréntesis. Ruby trata estos términos como si fuera una
declaración de asignaciones anidadas. Extrae los correspondientes valores
derechos, asignandolos a los términos en paréntesis, antes de continuar con
las asignaciones de mayor nivel.
class Bowdlerize
def initialize(aString)
@value = aString.gsub(/[aeiou]/, '*')
end
def +(other)
Bowdlerize.new(self.to_s + other.to_s)
end
def to_s
@value
end
end
a = Bowdlerize.new("damn ") » d*mn
a += "shame" » d*mn sh*m*
ASIGNACIÓN EN C++
El comportamiento por defecto en C++ es copiar cada miembro del objeto
Es posible hacer sobrecarga del operador =
En este caso el programador determina lo que ocurrirá cuando se asigna un
objeto a otro
No se debe confundir la asignación con la inicialización (constructores de
copia)
binarios ya vistos. Estos son: +=, -=, *=, /=, %=, &=, |=, ^=, <<= y >>=.
Nótese que no hay versiones compuestas para los operadores binarios && y ||.
Otros dos operadores de asignación incluidos son los de incremento(++) y
decremento (--) Estos operadores permiten, respectivamente, aumentar y
disminuir en una unidad el valor de la variable sobre el que se aplican. Así,
estas líneas de código son equivalentes:
temperatura = temperatura + 1; // Sin usar asignación compuesta
ni
incremento
temperatura += 1; // Usando asignación compuesta
temperatura++; // Usando incremento
ASIGNACIÓN EN JSCRIPT.
En JScript, el operador de asignación se utiliza para asignar un valor a una
variable. El operador de igualdad compara dos valores.
Al igual que muchos lenguajes de programación, JScript utiliza el signo igual
(=) para asignar valores a variables: es el operador de asignación. El operando
situado a la izquierda del operador = debe ser un valor Lvalue, es decir, una
variable, un elemento de la matriz o una propiedad de objeto.
El operando situado a la derecha del operador = debe ser un valor Rvalue.
Puede utilizarse cualquier tipo de valor arbitrario como valor Rvalue, incluido
el valor resultante de una expresión.
Ejemplo:
anInteger = 3;
JScript interpreta esta instrucción de la forma siguiente:
"Asignar el valor 3 a la variable anInteger"
o bien
"anInteger recibe el valor 3".
ASIGNACIÓN EN PHP
Ejemplo:
000 <?
001 $miVariable = 'suValor';
002
?>
000 <?
001 $a = 1;
002 $a += 1; // Sumamos y asignamos
003 $a = $a + 1; // Operacion equivalente
004 ?>
En PHP3, las variables siempre se asignan por valor. Esto significa que cuando
se asigna una expresión a una variable, el valor íntegro de la expresión original
se copia en la variable de destino. Esto quiere decir que, por ejemplo, después
de asignar el valor de una variable a otra, los cambios que se efectúen a una de
esas variables no afectará a la otra.
PHP4 ofrece otra forma de asignar valores a las variables: asignar por
referencia. Esto significa que la nueva variable simplemente referencia (en
otras palabras, "se convierte en un alias de" o "apunta a") la variable original.
Los cambios a la nueva variable afectan a la original, y viceversa. Esto
también significa que no se produce una copia de valores; por tanto, la
asignación ocurre más rápidamente.
Para asignar por referencia, simplemente se antepone un ampersand (&) al
comienzo de la variable cuyo valor se está asignando (la variable fuente).
Veamos un ejemplo:
2.1 CONCEPTO
llamadas individuales, los datos son procesados por los procedimientos y, una vez
que el programa ha terminado, los datos resultantes son presentados. Así, el flujo
de datos puede ser ilustrado como una gráfica jerárquica, un árbol, como se
muestra en la Gráfica 2.
PROGRAMA
PROGRAMA PRINCIPAL
(data)
Parámetro formal
Nombre
Ventajas
Los errores son mas rápidos de encontrar
Son menos frecuentes los errores
Búsqueda mas exacta de errores
Independencia de la arquitectura física de la computadora (portabilidad), esto
significa que un mismo lenguaje puede funcionar (al menos en teoría) en
distintos computadores, por lo que tanto el lenguaje como los programas
escritos con él serán transportables de un computador a otro. En la práctica,
esta característica resulta limitada por la gran diversidad de versiones y
dialectos que se constituyen para cada lenguaje.
una sentencia en un lenguaje de alto nivel da lugar, al ser traducida, a varias
instrucciones en lenguaje máquina. Se llaman de procedimientos porque están
diseñados para expresar la lógica capaz de resolver problemas generales.
Desventajas
Las características comunes entre dos entes no quedan explícitas.
El mundo en que vivimos se halla plagado de objetos: aviones, trenes,
automóviles, teléfonos, libros, computadoras, etc. Sin embargo, en esa época
las técnicas de programación no reflejaban esto. Lo procedural
[procedimientos] fue el paradigma principal de la programación, el cual define
un programa como un algoritmo escrito en algún lenguaje de programación.
Las razones de este énfasis son principalmente históricas, por lo tanto: con el
paradigma procedimental es más difícil modelar el mundo real.
La programación por procedimientos es adecuada para ciertas cosas, pero a
medida que los programas se van haciendo más complicados, y las
interacciones entre procedimientos más complejas, la programación mediante
procedimientos se hace muy dura de mantener, difícil de depurar errores y por
lo tanto muy compleja de actualizar.
Otra de las desventajas de la programación basada en procedimientos fue la
aparición de pantallas gráficas y el interés subsecuente en las aplicaciones
utilizando ventanas. El acceso a una interfaz gráfica de usuarios (GUI) donde
Ejemplo:
uses crt;
var num:integer; {variable global del programa}
Ejemplo:
2.9.1. PUNTEROS
El valor de cada variable está almacenado en una dirección de memoria.
Operador dirección (&):
Permite determinar la posición de memoria donde se encuentra una
variable. Ej int a; ... &a
Un puntero “apunta” a una variable si su contenido es la dirección de esa
variable.
Tipo de datos puntero: Contiene direcciones de variables. Una variable
de tipo puntero contendrá la dirección de memoria de otra variable.
Operador indirección: Permite acceder al contenido de la variable a la que
“apunta” la variable de tipo puntero.
Cada variable de un programa tiene una dirección en la memoria del
ordenador. Esta dirección indica la posición del primer byte que la
variable ocupa. En el caso de una estructura es la suma del tamaño de
cada uno de sus campos. Como en cualquier caso las variables son
almacenadas ordenadamente y de una forma predecible, es posible
acceder a estas y manipularlas mediante otras variables que contenga su
dirección. A este tipo de variables se les denomina punteros.
3.2.1. OBJETOS
Entidad de la vida real que tiene atributos (datos) y métodos (operaciones) que
operan sobre esos atributos. A los datos que forman parte del objeto se les
conoce como datos miembros y a las funciones como funciones miembros.
Los datos quedan ocultos al programador y únicamente dispondrá de las
funciones para acceder a ellos. Es una abstracción que se usa para representar
una entidad real. Todo objeto tiene estado, exhibe un comportamiento bien
definido y posee identidad única. Para crear objetos es necesario contar con
otro objeto que pueda crear objetos. El objeto creador de objetos se llama clase
y los objetos creados se llaman instancias.
Encapsulamiento
Porción visible: interfaz (protocolo)
Contrato público de comportamiento
Descripción de operaciones: información de entrada y de salida
Porción oculta: implementación
Estructura de datos para almacenar la información
Código que se ejecuta para realizar las operaciones
Invariantes de datos
Los datos de un objeto son inaccesibles desde el exterior, así que la
asignación de valores iniciales para estos datos pertenece al código del
objeto.
Estos valores iniciales se necesitan para establecer invariantes de datos
cuando se crea el objeto.
3.2.2. CLASE
Modelo que se usa para describir objetos similares. Es un tipo de dato definido
por el usuario que determina las estructuras de datos que lo forman y las
funciones asociadas con él, es decir es una descripción de un conjunto de
objetos casi idénticos. Una clase consta de métodos y datos que resumen las
características comunes de los objetos, incluyendo una descripción de cómo
crear un nuevo objeto de la clase. En otras palabras, las clases contienen los
anteproyectos para crear objetos.
Ejemplo
class Point {
private :
int xVal, yVal;
public:
void SetPt(int,int);
void OffsetPt(int,int);
};
3.2.3. MENSAJE
Es una petición de un objeto a otro para que este se comporte de una
determinada manera, ejecutando uno de sus métodos.
El nombre del método y
Los argumentos del método.
Por consecuencia, la invocación de un método es solamente una reacción
causada por el hecho de recibir un mensaje. Esto solamente es posible si el
método es realmente conocido por el objeto.
Para proporcionarte una comprensión acerca de esta comunicación, Tenemos
la siguiente clase (Definida en Lenguaje C):
class Integer {
attributes:
int i
methods:
setValue(int n)
Integer addValue(Integer j)
}
Nosotros podíamos crear nuevos objetos e invocar métodos sobre ellos. Por
ejemplo, podíamos usar:
El primer suceso dice: "Se crean los objetos cuando se necesitan." y el tercer
suceso indica: "Se borran los objetos cuando ya no son necesarios y se recupera la
memoria.". Estos sucesos se desarrollan con los constructores y destructores.
Constructores.
Los constructores son procedimientos de la clase que permiten crear objetos.
Un constructor es llamado para asignar memoria a un objeto, para asignar
valores a los datos del objeto y realizar tareas iniciales para un nuevo objeto.
Esto implique que si no podemos trabajar con un objeto que no haya sido
creado a través de un constructor y si sólo se pueden modificar mediante los
procedimientos de la clase, no tenemos forma de corromper el objeto, lo
cual aumenta la confiabilidad y facilita la rehusabilidad.
Destructores
Un destructor es un procedimiento de la clase que realiza la tarea opuesta a su
constructor, libera la memoria que fue asignada al objeto que fue creado por el
constructor. Es deseable que el destructor se invoque implícitamente cuando
el objeto abandone el bloque donde fue declarado.
El destructor le permite al programador despreocuparse de tener que liberar la
memoria que deja de utilizar y correr el riesgo de que ésta se sature.
Normalmente en los lenguajes con orientación a objetos el destructor como el
constructor tienen el mismo nombre de la clase a la que pertenece.
Ejemplo:
Constructor iniciar (real 1, real 2)
x = real 1
y = real 2
Destructor iniciar
borra x
borra y
Operación Resultado
3+7 12
0.1+11.001 11.101
“ho” + “la” “hola”
Un tipo de objeto puede tener subtipos. Por ejemplo, el tipo de objeto persona
puede tener subtipos estudiante y empleado. A su vez, el tipo de objeto estudiante
puede tener como subtipo estudiante de pregrado y estudiante de postgrado,
Una clase implanta el tipo de objeto. Una subclase hereda propiedades de su clase
padre; una sub-subclase hereda propiedades de las subclases; etc. Una subclase
puede heredar la estructura de datos y los métodos, o algunos de los métodos, de
su superclase. También tiene sus métodos e incluso tipos de datos propios.
Superclase
Subclase
Ejemplo:
Superclase Subclase
class Point { class Circle inherits from Point {
attributes: atrributes:
int x, y int radius
methods: methods:
setX(int newX) setRadius(int newRadius)
getX() getRadius()
setY(int newY) }
getY()
}
La clase Circle hereda todos los elementos de datos y métodos de la clase Point.
No hay necesidad de definirlos dos veces: Solamente usamos los ya existentes (y
familiares) datos y definiciones de métodos.
Al nivel de objeto ahora podemos usar un círculo justamente como habríamos
usado un punto, debido a que un círculo es-un(a) punto. Por ejemplo, podemos
definir un objeto círculo y establecer sus coordenadas del punto central :
Circle acircle
Herencia simple.
Un objeto puede extender las características de otro objeto y de ningún
otro, es decir, solo puede tener un padre, aquí se encuentran los lenguajes
Java, Ada y C#.
Herencia múltiple:
Un objeto puede extender las características de uno o más objetos, es decir,
puede tener varios padres. Esto permite que la subclase herede propiedades
de más de una superclase y "mezclar" sus propiedades.
En este aspecto hay discrepancias entre los diseñadores de lenguajes.
Algunos de ellos han preferido no admitir la herencia múltiple por las
posibles coincidencias en nombres de métodos o datos miembros.
Si la clase A hereda de más de una clase, por ej. A hereda de B1, B2, ...,
Bn, hablamos de herencia múltiple. Esto puede presentar conflictos de
nomenclatura en A si al menos dos de sus superclases definen
propiedades con el mismo nombre.
La definición de arriba presenta conflictos de nomenclatura los cuáles
ocurren si más de una superclase de una subclase usan el mismo nombre
para ambos, atributos o métodos. Por ejemplo, supongamos que la clase
String define un método setX() que pone el string en una secuencia de "X"
caracteres. Se produce la pregunta ¿Que debería ser heredado por
DrawableString(clase hija)? ¿La versión de Point(clase definida en el
ejemplo de la seccion 3.6 HERENCIA DE DATOS), de String o ninguna
de las dos?
Estos conflictos pueden ser resueltos de al menos dos maneras:
El orden en el cuál las superclases son provistas, definen que propiedad
será accesible por el nombre causante del conflicto. Los otros quedarán
"escondidos".
Las subclases deben resolver el conflicto proveyendo una propiedad
con el nombre y definiendo como usar los de sus superclases.
3.7.1. PROCEDIMIENTOS.
Los procedimientos han estado en uso desde los primeros días de la
computación. Entre los beneficios de los procedimientos se hallan los
siguientes:
Abstracción de operaciones.
El usuario solo necesita saber lo que el procedimiento hace y no como
esta implementado. Los procedimientos pueden usarse para dividir un
programa de manera que las operaciones en el pueden entenderse
aisladamente.
3.7.2. MÓDULOS.
Un módulo divide el texto de un programa en piezas manejables.
Los módulos son estáticos, no podemos crear dinámicamente módulos
nuevos o copias de las existentes mientras se ejecuta un programa.
Un módulo sirve como una caja negra con la cual el resto del programa
interactúa a través de una interfaz.
La interfaz de un módulo es una declaración de tipos, variables,
procedimientos, etc.
Ejemplo:
3.7.3. CLASES.
Podemos crear y destruir objetos mientras se ejecuta el programa. Cada clase
tiene un procedimiento constructor el cual se invoca para dar valores iniciales
a un objeto de la clase recién creada, y un procedimiento destructor el cual se
invoca justo antes de que el objeto desaparezca.
4.1 CONCEPTO
4.2 EL OBJETIVO
La diferencia entre ambos estriba en que los lenguajes funcionales híbridos son
menos dogmáticos que los puros, al permitir conceptos tomados de los lenguajes
imperativos, como las secuencias de instrucciones o la asignación de variables. En
contraste, los lenguajes funcionales puros tienen una mayor potencia expresiva,
conservando a la vez su transparencia referencial, algo que no se cumple siempre
con un lenguaje híbrido.