Monografias.com > Sin categoría
Descargar Imprimir Comentar Ver trabajos relacionados

Tópicos de Lenguajes de programación (página 2)



Partes: 1, 2, 3

2.
Especificación de sintaxis y
semántica

SINTAXIS
Un programa en
cualquier lenguaje se
puede concebir como un string de caracteres escogidos de
algún conjunto o alfabeto de caracteres. Las reglas que
determinan si un string es un programa válido o no,
constituyen la sintaxis de un lenguaje. Posteriormente, se
estudiarán ciertas notaciones denominadas expresiones
regulares y gramáticas libres de contexto, muy
usadas no sólo para especificar las sintaxis de los
lenguajes de
programación sino también para contribuir en
la construcción de sus compiladores.

SEMÁNTICA
Las reglas que determina el significado de los programas
constituyen la semántica de los lenguajes de programación. Es más
difícil de especificar que la sintaxis.

Los siguientes son algunos enfoques para
especificar la semántica de los
lenguajes:

Semántica Interpretativa (u
Operacional)

Un lenguaje de máquina tiene su
semántica definida por el computador.
Un programa en lenguaje de máquina "significa"
exactamente lo que el computador hace cuando el programa
"corre" o se ejecuta. Sin embargo, con un lenguaje de alto
nivel no se puede dejar que el computador defina la
semántica del lenguaje, puesto que no es posible "correr
programas y ver" hasta que se tenga un compilador. No se puede
tener un compilador y saber qué es correcto hasta haber
definido lo que los programas significan.
Este enfoque interpretativo para definir la semántica de
los lenguajes de programación consiste en postular una
máquina abstracta y proveer reglas para la
ejecución de programas sobre esta máquina
abstracta. Así, estas reglas definen el significado de
los programas. Usualmente, la máquina abstracta se
caracteriza por un estado
consistente de todos los objetos datos, sus
valores y
los programas con sus contadores de programa. Las reglas
semánticas especifican cómo el estado es
transformado por las diversas construcciones de los lenguajes
de programación.

Traducción
La traducción de un lenguaje assembly a
lenguaje de máquina (que es directa y comprensible),
forma una especificación semántica muy usada para
un lenguaje assembly.

El compilador para un lenguaje de alto nivel
sobre una máquina específica llega a ser la
definición semántica del
lenguaje.

Definición
Axiomática

Se pueden definir reglas que relacionan los
datos antes y después de la ejecución de cada
programa. Estas reglas se pueden usar para proveer teoremas
acerca de la relación E/S de un programa. Este enfoque
tiene la ventaja que puede usarse para definir
semánticas para un aparte más que para todos los
aspectos de un lenguaje.

Definición
Extensible

En este enfoque se definen ciertas operaciones
primitivas y el significado del lenguaje en términos de
estas primitivas. Ejemplo LISP.

Semántica Matemática o
Denotacional

Los objetos matemáticos corresponden a
programas que son definidos y reglas abstractas para traducir
programas a estos objetos abstractos.

LA ESTRUCTURA
JERÁRQUICA DE LOS LENGUAJES DE
PROGRAMACIÓN

Un lenguaje de
programación es una notación para especificar
una secuencia de operaciones a realizar sobre objetos dados.
Estos se pueden agrupar en una jerarquía de
árbol, cuyas unidades son comunes y familiares a la
mayoría de los lenguajes.

En el tope de la jerarquía está
el programa propiamente tal, que es la unidad de
ejecución básica. Luego viene una entidad que
puede ser compilada y que se compone de sentencias y
expresiones.

LENGUAJES DE ALTO
NIVEL

Un lenguaje de programación es un medio
para comunicar algoritmos
al computador. Existen cientos de lenguajes de
programación los cuales difieren en el grado de clausura
a un lenguaje natural o matemático por una parte, y al
lenguaje de máquina por otra. También difieren en
el tipo de problema para el cual funcionan mejor.
Algunos aspectos de los lenguajes de alto nivel que los hacen
preferibles a los lenguajes de máquina o assembly
son:

Facilidad de
Entender
. Un programa en lenguaje de alto
nivel es: fácil de leer y de escribir la notación
más natural de describir algoritmos.

Un buen lenguaje de programación
debería proveer aspectos para el diseño modular contemplando operadores,
estructura de
datos, flujo de control.

Naturalidad.
Facilidad de poder
expresar un algoritmo en
el lenguaje
(sentencias estructuradas, estructura natural del
algoritmo)

Portabilidad.
Posibilidad de ejecutar programas en una variedad de máquinas.

Eficiencia de Uso.
Considerar aspectos del lenguaje como de los programas.
Compilación eficiente.

Existen aspectos que hacen más legibles
los programas en lenguajes de alto nivel:

Estructuras de Datos. Reglas de Alcance
que permitan modificaciones en partes de un programa sin
afectar otras partes del mismo.

Flujo de Control que identifique claramente las
estructuras
de ciclo de un programa Subprogramas que permitan diseño
modular.

Un compilador también puede ayudar a
producir programas confiables. Por ejemplo, un compilador puede
chequear si los tipos de operandos son compatibles; puede
advertir al usuario si hay posibilidad de usar una variable
antes de ser definida, o si una referencia a un arreglo puede
estar fuera de los límites.

ELEMENTOS SINTÁCTICOS DE UN
LENGUAJE DE
PROGRAMACIÓN

Conjunto de Caracteres

Es la primera etapa en el diseño de la sintaxis
de un lenguaje. Casi todos tienen el mismo conjunto de letras y
dígitos. La diferencia está en los caracteres
especiales Incide en la determinación del tipo de equipo
para la E/S

Identificadores
Sintaxis básica: string de letra y/o dígito
comenzando con letra

Se introducen caracteres especiales que
facilitan la lectura

Operadores
La mayoría de los lenguajes tiene un conjunto
estándar de operadores:
aritméticos
relacionales
lógicos
Palabras Clave y
Reservadas

Palabra clave: identificador usado como parte fija de
la sintaxis de una sentencia.
Palabra reservada: palabra clave que no puede ser usada como
identificador.

El análisis sintáctico se facilita
usando palabras reservadas

Generalmente una sentencia comienza con palabras
claves designando el tipo de sentencia.

Comentarios y Palabras
Opcionales

La mayoría de los lenguajes permite
incluir comentarios en los programas
Los comentarios no son considerados durante la
compilación.

Espacios en
Blanco

Varía su inclusión entre un
lenguaje y otro

Delimitadores
Es un elemento sintáctico para marcar el
comienzo y/o el fin de una unidad
sintáctica

Ejemplo

BEGIN
….
END
Las características son: realza la lectura
simplifica el análisis sintáctico elimina
ambigüedades delimita explícitamente frontera o
cota de una construcción sintáctica.

Formato Libre y
Fijo

Una sintaxis es libre si las sentencias del
programa se pueden escribir en cualquier parte de una
línea sin observar la posición o salto de
línea. Ejemplo Pascal

Una sintaxis es fija si utiliza la
posición en la línea para proporcionar información.

Ejemplo estrictamente fija, máquina;
parcialmente fija, Fortran

Expresiones
Son unidades sintácticas básicas con las cuales
se construyen sentencias. Hay distintas formas para escribir
una expresión: infija, prefija, postfija (inorden,
preorden, postorden).

Sentencias
Son las componentes sintácticas más importantes
de los lenguajes de programación. Algunos tienen formato
básico de sentencia. Existen diferentes sintaxis para
cada tipo diferente de sentencia.

En general existen sentencias simples y
estructuradas.

Estructura de Programa y
Subprograma

La organización sintáctica de un
programa y la definición de subprograma es
variada.

Definición variada de
subprograma

  • cada definición de subprograma es
    tratada como una unidad sintáctica
    separada
  • Las principales estructuras de los programas
    son: bloques, subprogramas
  • El bloque se ejecuta cuando la secuencia de
    ejecución llega. El procedimiento
    es llamado explícitamente.

En Fortran cada subprograma es compilado separadamente
y los programas son "linkeados" en tiempo de
carga.

En APL los subprogramas son compilados separadamente y
son "linkeados" sólo cuando uno llama a otro durante la
ejecución.

Definición de subprogramas
anidados

Un ejemplo se da en ALGOL que muestra la
estructura de programas anidados en que la definición
del subprograma aparece como declaración en el programa
principal. A su vez, el mismo subprograma puede contener otra
definición anidada.

Descripción separada de datos y
sentencias ejecutables

Un representante es el COBOL. Las
declaraciones de datos y sentencias ejecutables para los
subprogramas (párrafos), se encuentran en la DATA
DIVISION y en la PROCEDURE DIVISION, respectivamente. La
ENVIRONMENT DIVISION consiste de las declaraciones de
operación y ambiente
externo, equipo, archivos,
memoria, etc…
Todos los datos son globales a los subprogramas. Una ventaja es
que cambios en la estructura de datos se hacen sin tocar los
subprogramas.

Definición de subprogramas no
separados

No hay diferencia sintáctica entre las
sentencias del programa y las del subprograma. Un ejemplo
SNOBOL: independiente del número de subprogramas que
contenga el programa, es una lista de sentencias. El punto
donde un subprograma comienza y termina no se distingue. Los
programadores introducen una separación artificial
insertando comentarios. Otro ejemplo BASIC.

3. Tipos de datos,
alcance y tiempo de vida de variables

  • Datos

Dato: "hecho o valor a partir
del cual se puede inferir una conclusión;
información"

Los datos son aquello que un programa manipula. Sin
datos un programa no funcionaría correctamente. Los
programas manipulan datos de manera muy diferente según el
tipo de dato del que se trate. Y hay varios de estos
tipos:

  • Tipos de Datos
  • Cadenas de Caracteres

Ya hemos visto este tipo de datos. Son literalmente
cualquier cadena o secuencia de caracteres que puedan imprimirse
en la pantalla. (De hecho pueden ser también ciertos
caracteres de control que no son imprimibles).

En Python las cadenas pueden representarse de varias
formas:

Con comillas simples:

'Esta es una cadena'

Con comillas dobles:

"Esta otra es una cadena muy similar"

Con tres comillas dobles:

""" Esta es una cadena muy larga que puede

Ocupar varias líneas si así lo deseamos
y

Python la mantendrá del mismo modo en
que

Un uso especial de esta última forma puede verse
en la generación de la documentación de las funciones de
Python creadas por nosotros mismos, algo que veremos más
adelante.

Podemos acceder a los caracteres individuales de una
cadena al tratarla como una matriz de
caracteres. Usualmente hay varias operaciones que el lenguaje de
programación provee para ayudarnos a manipular las
cadenas, tales como buscar una subcadena dentro de otra, unir dos
cadenas, copiar una cadena en otra, etc.

  • Enteros

Números enteros desde un valor negativo alto
hasta otro valor positivo alto. El valor máximo se conoce
como MAXINT y depende de la cantidad de bits utilizados en
la computadora
para representar un número. En la mayor parte de las
computadoras
actuales esta cantidad es de 32 bits, lo que implica que MAXINT
se acerque a los dos billones.

También podemos utilizar enteros sin signo
lo que incluye números positivos y el cero. De esta manera
el número máximo alcanzable equivale a dos por
MAXINT, o cuatro billones en una computadora de 32
bits.

Dado que el tamaño de los enteros está
restringido a MAXINT, cuando sumamos dos enteros cuyo total es
mayor que MAXINT, el resultado obtenido es incorrecto. En algunos
lenguajes y sistemas el
resultado incorrecto igual se devuelve (usualmente con
algún tipo de aviso secreto que uno puede revisar si cree
que pudo haber habido algún problema). Normalmente en
estos casos se produce un error, el cual será manejado por
el programa o directamente éste finalizará. Pithon
utiliza este último sistema, mientras
que TCL ha adoptado el primero. BASIC produce un error pero no
provee ningún método
para tratarlo (al menos yo no sé cómo).

  • Números Reales

Estos son las fracciones. Pueden representar
números muy altos, más altos que MAXINT pero con
menor precisión. Esto quiere decir que dos números
reales que deberían ser idénticos pueden no serlo
cuando son examinados por la
computadora. Esto se debe a que la computadora trabaja por
aproximación en los más mínimos detalles. De
esta forma 4,0 podría ser representado como 3,9999999….
o 4.00000001. Estas aproximaciones son lo suficientemente
precisas para la mayor parte de nuestros objetivos,
pero ocasionalmente pueden ser importantes para alguna tarea
específica. Recuerda esto si obtienes un resultado
extraño al utilizar números reales.

  • Números Complejos o
    Imaginarios

Si tienes una formación científica o
matemática seguramente los conocerás muy bien. Si
este no es tu caso, lo más probable es que ni siquiera
hayas escuchado hablar de los números complejos. De todos
modos, algunos lenguajes de programación -Fortran, por
ejemplo- permiten trabajar con números complejos. La mayor
parte del resto, como Python, proveen una librería de
funciones que permiten operar con números complejos. Y
antes de que preguntes, lo mismo se aplica para las matrices.

  • Valores Booleanos – Verdadero y
    Falso

Como indica el encabezado, este tipo presenta
sólo dos valores: verdadero o falso. Algunos
lenguajes manipulan los valores
booleanos directamente, mientras que otros usan una
convención por medio de la cual un valor numérico
(en general 0) representa 'falso' y otro (1 o -1) equivale a
'verdadero'.

En general se conoce a los valores booleanos como
"valores de verdad" debido a que son utilizados para comprobar si
algo es verdadero o falso. Por ejemplo, si escribimos un programa
que realice backups de todos los archivos en un directorio, lo
que debemos hacer es copiar un archivo y luego
preguntarle al sistema operativo
por el nombre del siguiente archivo. Si no hay más
archivos responderá con una cadena vacía, entonces
podremos comprobar que la cadena está vacía y
guardar el resultado como un valor booleano (verdadero si
está vacía). Pronto verás cómo
utilizaremos este resultado más adelante en este
curso.

  • Colecciones

Las ciencias de la
Computación han creado una disciplina en
sí misma para estudiar las colecciones y sus diversos
comportamientos. Algunos de los nombres que podrás
encontrar son:

Matrices o vectores

Una lista de ítems que pueden ser indexados para
una recuperación sencilla y rápida. Usualmente es
necesario aclarar de entrada cuántos ítems deseamos
guardar en la matriz. Por ejemplo, si tenemos un vector llamado
A, podemos recuperar su tercer ítem escribiendo A[3]. (En
realidad, generalmente los vectores
comienzan en la posición 0, por lo cual deberíamos
escribir A[2]). Las matrices o vectores son fundamentales en
BASIC ya que son la única colección predeterminada.
En Python las matrices se simulan por medio de listas (ver
más abajo) y Tcl implementa las matrices como diccionarios
(ver más abajo).

Listas

Una lista es una secuencia de ítems. La
diferencia con los vectores es que una lista puede seguir
creciendo al agregársele un nuevo ítem. En general
no están indexadas, por lo cual se hace necesario buscar
el ítem requerido recorriendo desde el principio al fin
toda la lista y evaluando cada elemento para ver si es el que
nosotros buscamos. Tanto Python como Tcl trabajan con listas,
mientras que en BASIC debemos utilizar algunos trucos para
simularlas. Los programadores de BASIC en general crean matrices
muy grandes para superar esta debilidad. Python permite indexar
las listas -en realidad estrictamente no maneja vectores, pero
combina en un mismo elemento la posibilidad de indexación
de los vectores con la habilidad para crecer de las listas. Como
veremos pronto esta característica es realmente muy
útil.

Pilas

Piensa en una pila de bandejas en un restaurant: un
asistente coloca una pila de bandejas limpias sobre las que ya
había antes, y estas son tomadas una por una por los
clientes. De esta
manera, las bandejas que quedan abajo de todo son las menos
utilizadas (y a veces no les llega nunca la oportunidad de ser
usadas). Las pilas de datos
funcionan del mismo modo: se agrega un dato a la pila o se retira
uno de ella, pero el dato retirado es siempre el último
que se colocó en la pila. Esta propiedad de
las pilas se denomina con frecuencia First In Last Out ("el
primero es el último")
o FILO. Una
característica útil de las pilas es que se puede
revertir una lista de ítems al colocarla en la pila y
luego retirarla. El resultado será una lista inversa
respecto de la original. Las pilas no son tipos predeterminados
en Python, Tcl o Basic, por lo que es necesario crear una
función
para implementarlas. Sin embargo, las listas son en general el
mejor punto de partida, ya que pueden crecer -igual que las
pilas- según sea necesario.

Bolsas

Una bolsa es una colección de ítems sin un
orden específico y que puede contener duplicados. Las
bolsas tienen en general operadores que nos permiten agregar,
buscar y borrar los ítems. En Python y en Tcl las bolsas
son simplemente listas. En BASIC es necesario crear la bolsa a
partir de una matriz grande.

Conjunto

Un conjunto tiene la propiedad de guardar
únicamente un miembro de cada ítem. En estos, se
puede comprobar si un ítem pertenece al conjunto
(pertenencia), y agregar, remover u obtener ítems o unir
dos conjuntos
según la teoría
matemática de los conjuntos (unión,
intersección, etc.). Ninguno de los lenguajes estudiados
aquí implementa conjuntos directamente, pero pueden ser
utilizados en Python y en Tcl gracias al tipo de datos llamado
diccionario.

Cola

Una cola es similar a una pila excepto que el primer
elemento de una cola es el primero en ser retirado. Esto se
conoce como First In First Out ("el primero es el
primero")
o FIFO.

Diccionarios

Un diccionario combina las propiedades de las listas,
los conjuntos y los vectores. Es posible seguir agregando
elementos (como en las listas) y también acceder a los
ítems mediante una clave provista en el punto de
inserción (como con los vectores). Debido a que el acceso
a los datos se realiza por medio de una clave, ésta debe
ser necesariamente única ya que si no se perdería
la referencia (como en los conjuntos). Los diccionarios son
estructuras inmensamente útiles y son tipos de datos
predeterminados tanto en Python como en Tcl. En BASIC son muy
poco utilizados dadas la dificultad de implementarlos de manera
eficiente.

Podemos utilizar los diccionarios de muchas maneras y
más adelante veremos varios ejemplos. Por ahora, veamos
cómo crear un diccionario en Python, cómo agregar
algunos datos y cómo recobrarlos:

>>> dict = {}

>>> dict['booleano'] = "Un dato cuyo valor
puede ser verdadero o falso"

>>> dict['entero'] = "Un número
entero"

>>> print dict['booleano']

Sencillo ¿no?

Hay muchos otros más pero estos son los
principales con los que trabajaremos en este curso (de hecho,
sólo utilizaremos algunos de estos).

  • Archivos

Como usuario de computadoras sabrás todo acerca
de los archivos, la base de prácticamente todo lo que
hacemos con una computadora. No es sorprendente entonces que la
mayor parte de los lenguajes de programación incluyan un
tipo especial de datos llamado archivo. Dado que los
archivos y su procesamiento son tan importantes, dejaré
para más adelante su discusión en una
sección especial.

  • Fecha y Hora

La fecha y la hora a veces se incluyen como
predeterminados en algunos lenguajes. En otros casos son
representados simplemente por un número alto
(típicamente el número de segundos a partir de una
determinada fecha u hora). Para otros lenguajes este tipo se
representa de manera compleja, como veremos en la próxima
sección. Este procedimiento vuelve más sencillo
recuperar el mes, el día o la hora.

  • Tipos Complejos/Definidos por el
    Usuario

Muchas veces los tipos básicos descriptos
más arriba no son adecuados para determinada tarea aunque
los combinemos por medio de colecciones. En ocasiones deseamos
agrupar varios datos juntos y tratarlos como si fueran un solo
elemento. Un ejemplo de esta situación podría ser
la descripción de una dirección postal: la calle, el
número, la ciudad y el código
postal. La mayor parte de los lenguajes nos permiten agrupar
estos datos en un registro o estructura.

En BASIC un registro de este
tipo se realiza así:

Type Dirección

Numero_Casa AS INTEGER

Calle AS STRING

Ciudad AS STRING

Cod_Postal AS STRING

End Type

En Python es algo diferente:

class Dirección:

def __init__(self, Casa, Calle, Ciudad, Codigo):

self.Numero_Casa = Casa

self.Calle = Calle

self.Ciudad = Ciudad

self.Cod_Postal = Codigo

Puede parecerte medio esotérico, pero no te
preocupes que pronto tendrá su sentido.

Veremos cómo utilizar estas estructuras en
nuestra próxima sección dedicada a las variables.

  • Variables

Los datos son almacenados en la memoria de
la computadora. Podemos comparar este proceso con
las casillas de correo donde se colocan las cartas. Uno
podría colocar una carta en
cualquier casilla, pero si estas no tienen una etiqueta que las
identifique, resultará prácticamente imposible
recuperar la carta. Para
seguir con la comparación, las variables son las etiquetas
de las casillas en la memoria de la computadora.

Ahora ya conocemos qué son los datos, pero
¿qué podemos hacer con ellos? Desde el punto de
vista de la programación podemos crear instancias
de los datos (organizados en objetos) y asignarlas a
variables. Una variable es una referencia a un área
específica de la memoria de la computadora donde se
guardan los datos. En algunos lenguajes de programación la
variable debe coincidir con el tipo de dato al cual apunta. En
BASIC, por ejemplo, declaramos una variable de cadena
agregándole el signo $ al final del nombre:

DIM MICADENA$

MICADENA$ = "Esta es una cadena"

En este ejemplo DIM
MICADENA$ crea la referencia y especifica el espacio
para almacenar la cadena (y sabemos que se trata de una cadena
por el signo $). La línea MICADENA$ = "Esta es…" define los datos
y los coloca en el espacio de la memoria denominado
MICADENA$.

De forma similar declaramos un entero mediante el signo
% al final del nombre:

DIM MIENTERO%

MIENTERO% = 7

En Python y en Tcl una variable adquiere el tipo de
datos que se le asigna por primera vez y lo mantendrá
durante el programa, avisándonos si intentamos mezclar los
datos de manera extraña, tal como sumar una cadena a un
número (¿Recuerda el ejemplo que vimos del mensaje
de error?). Es posible cambiar el tipo de datos de una variable
en Python reasignando la variable.

>>> q = 7

>>> print 2*q

14

>>> q = "Siete"

>>> print 2*q

SieteSiete

Notemos que inicialmente la variable q apuntaba al
número 7 y mantuvo este valor hasta que reasignamos la
variable con el valor "Siete". De esta manera las variables en
Python mantienen cualquier tipo de datos siendo posible modificar
la referencia hacia otro tipo de datos simplemente reasignando la
variable. Cuando se produce la reasignación el dato
original se pierde y Python lo borrará de la memoria
(salvo que se lo recupere en otra variable). A esto se denomina
"recolección de basura". (Esto puede compararse con
el empleado del correo que cada tanto revisa las casillas y
retira aquellos paquetes que carecen de información de
destino. Si nadie los reclama ni es posible encontrar a su
dueño, los paquetes son incinerados.)

BASIC no permite realizar esto. Si una variable es una
variable de cadena (terminada en $) jamás podremos asignar
a ella un número. De forma similar, es imposible asignar
una cadena a una variable de enteros (terminada en %). Por otra
parte, BASIC permite 'variables anónimas' (no tienen
ningún identificador después del nombre). Sin
embargo, este tipo de variables sólo puede contener
números enteros o reales.

Un ejemplo final con variables enteras en
BASIC:

i% = 7

PRINT 2 * i%

i% = 4.5

PRINT 2 * i%

Notemos que la asignación de 4.5 en la variable
i% parece funcionar, sólo que en realidad lo único
que fue asignado fue la parte entera del valor. Esto nos recuerda
la forma en que Python trata la división de enteros. Todos
los lenguajes de programación tienen sus pequeñas
idiosincracias como esta.

  1. Acceso a Tipos Complejos

Podemos también asignar un tipo de datos complejo
a una variable, pero para acceder a cada uno de los campos
individuales del tipo de datos deberemos utilizar algún
mecanismo de acceso, que será definido por el propio
lenguaje. Usualmente se trata de un punto.

Considerando el caso del tipo "dirección" que
hemos definido antes, en BASIC realizaríamos lo
siguiente:

DIM Direc AS Dirección

Direc.Numero = 7

Direc.Calle = "Los Rosales"

Direc.Ciudad = "Cualquiera"

Direc.Codigo = "123 456"

PRINT Direc.Numero," ",Direc.Calle

Y en Python:

Direc = Dirección(7,"Los
Rosales","Cualquiera","123 456")

print Direc.Numero, Direc.Calle

ALCANCE DE LAS VARIABLES

El alcance, o vida, de una variable determina qué
comandos de
secuencia de comandos pueden tener acceso a dicha variable. Una
variable declarada dentro de un procedimiento tiene alcance
local; la variable se crea y se destruye cada vez que se ejecuta
el procedimiento. No se puede tener acceso a ella desde fuera del
procedimiento. Una variable declarada fuera de un procedimiento
tiene alcance global; su valor es accesible y modificable desde
cualquier comando de secuencia de comandos de una página
ASP.

Nota: Al limitar el alcance de la variable
a un procedimiento mejorará el rendimiento.

Si declara variables, una variable local y una variable
global pueden tener el mismo nombre. La modificación del
valor de una de ellas no afecta al valor de la otra. Sin embargo,
si no declara las variables, podría modificar
inadvertidamente el valor de una variable global. Por ejemplo,
los siguientes comandos de secuencia de comandos devuelven el
valor 1 incluso aunque haya dos variables llamadas Y:

<% Option Explicit Dim Y Y = 1 SetLocalVariable
Response.Write Y Sub SetLocalVariable Dim Y Y = 2 End Sub
%>

Por el contrario, los comandos siguientes devuelven el
valor 2 porque las variables no se han declarado de forma
explícita. Cuando la llamada al procedimiento asigna a Y
el valor 2, el motor de
secuencias de comandos da por supuesto que el procedimiento
pretende modificar la variable global:

<% Option Explicit Dim Y = 1 SetLocalVariable
Response.Write Y Sub SetLocalVariable Y = 2 End Sub
%>

Para evitar problemas,
adquiera el hábito de declarar explícitamente todas
las variables. Lo cual es especialmente importante si utiliza la
instrucción #include para incluir archivos en su archivo
ASP. La secuencia de comandos incluida está contenida en
un archivo aparte, pero se trata como si formara parte del
archivo contenedor. Es muy fácil olvidarse de que hay que
utilizar nombres de variables diferentes en la secuencia de
comandos principal y en la secuencia de comandos incluida, a
menos que declare las variables.

Asignar a las variables alcance de sesión o de
aplicación

Las variables globales sólo son accesibles en un
mismo archivo ASP. Para hacer que una variable sea accesible en
varias páginas, asigne a la variable alcance de
sesión o de aplicación. Las variables con alcance
de sesión están disponibles en todas las
páginas de una aplicación ASP que pida un mismo
usuario. Las variables con alcance de aplicación
están disponibles en todas las páginas de una
aplicación ASP que pida cualquier usuario. Las variables
de sesión son una buena manera de almacenar
información para un único usuario, como sus
preferencias o el nombre o la identificación del usuario.
Las variables de aplicación son una buena manera de
almacenar información para todos los usuarios de una
determinada aplicación, como los saludos
específicos o los valores generales necesarios en la
aplicación.

ASP proporciona dos objetos integrados en los que puede
almacenar variables: el objeto Session y el objeto
Application.

También puede crear instancias de objetos con
alcance de sesión o de aplicación. Para obtener
más información, consulte Establecer el alcance de
los objetos.

Tiempo de vida de una variable:

El tiempo de vida se refiere al intervalo de tiempo que
trascurre desde que se crea la variable hasta que se destruye. En
Java, una
variable se crea en el momento que se ejecuta su
declaración y se destruye cuando finaliza el bloque de
instrucciones en donde fue declarada. Por ejemplo:

if ( … ) {

println( … );

int n= 123; // (A)

n= …; // (B)

} // (C)

La variable n se crea en el momento de ejecutar la
instrucción (A) y se destruye al encontrar el final del
bloque de instrucciones en (C). En (B) se asigna un nuevo valor a
la variable n, que ya existía.

Un mismo identificador se puede usar para nombrar varias
variables distintas. Por ejemplo en el siguiente
código:

{

int x= a+b;

… x …

}

{

int x= fun(a,b);

… x …

}

En el código anterior, la variable x del primer
bloque no tiene ninguna relación con la variable x del
segundo bloque. Se trata de dos variables que se identifican por
el mismo nombre. Se podría renombrar la variable x del
segundo bloque por y, sin cambiar en nada la ejecución del
programa.

Incluso, la declaración de una variable puede
aparecer una sola vez en el programa, pero aún así
servir para identificar varias variables durante la
ejecución del programa. Esto se aprecia en el siguiente
ciclo:

while ( … ) {

double x= 3.14; // (A)

x= …;

} // (C)

En cada iteración, en (A) se crea una nueva
variable x que se destruye en (C), pero todas se llaman con el
mismo nombre (x).

Los parámetros de una función
también son variables. Se crean en el momento que la
función es invocada y se destruyen al finalizar la
ejecución de la función. Por ejemplo, la siguiente
función calcula el máximo común
divisor:

int mcd(int x, int y) {

while (x!=y) {

if (x>y)

x= x-y;

else

y= y-x;

}

return x;

}

Considere las siguientes invocaciones de esta
función (por ejemplo en el procedimiento run):

int a= 15;

println( mcd(a, 21) );

println( mcd(25, a) );

En cada invocación de mcd se crean nuevas
variables x e y (sus parámetros) que se inicializan con
los valores de los argumentos. Esto se visualiza mejor en la
siguiente figura:

Observe en la figura que durante la ejecución del
programa se crean dos variables con nombre x. Esto no constituye
ninguna confusión en el momento de accesar la variable x,
puesto que ambas variables tienen tiempo de vista
disjuntos.

Observe también, que la función m.c.d.
modifica los parámetros x e y sin modificar el valor de la
variable a en ninguna de las invocaciones. Una función no
puede producir efectos laterales sobre las variables que se
especifiquen como argumentos durante la invocación. Sin
embargo, una función sí puede producir efectos
laterales sobre los objetos que se pasen como argumentos en la
invocación (como por ejemplo un arreglo).

Existen casos que sí existen varias variables con
el mismo nombre en un mismo instante. Por ejemplo, consideremos
el siguiente programa:

int x= 105;

println( mcd(210, x) );

Mientras se ejecuta la función mcd existen dos
variables x. ¿Qué variable se modifica al ejecutar
x= x-y;? Intuitivamente la respuesta es “la variable de mcd''.
Esto es cierto, pero para aclarar mejor este concepto
definiremos el alcance de una variable.

Alcance de una variable:

El alcance de una variable es el conjunto de
instrucciones en la que esa variable es visible por medio de su
identificador. En Java, el alcance de una variable está
delimitado por la primera instrucción que sigue a su
declaración en el programa, hasta el final del bloque en
donde fue declarada. Por ejemplo en:

void run() {

int x= 105; // (A)

println( mcd(210, x) );

} // (B)

La variable x es conocida desde (A) hasta (B). En
particular esa variable no es conocida en la definición de
m.c.d., ni en la definición de ningún otro
procedimiento porque esas definiciones no se encuentran entre (A)
y (B).

Es importante entender la diferencia entre alcance y
tiempo de vida de una variable. En el ejemplo anterior, mientras
se ejecuta la función m.c.d., la variable x del
procedimiento run continúa existiendo, a pesar de que no
es visible desde mcd. Si una variable no es visible, no
necesariamente ha sido destruida. En cambio, si una
variable fue destruida, esa variable no es visible.

4. Evaluación
de expresiones

En este capitulo se considerará como evaluar una
expresión que contiene varias operaciones, la forma
intuitiva de evaluar una expresión es evaluar una
operación a la vez en un orden apropiado, el resultado de
cada operación se materializa en una
relación temporal para su inmediata utilización. El
inconveniente de esta aproximación es la creación
de relaciones temporales que implican la escritura y
lectura de
disco. Una aproximación alternativa es evaluar operaciones
de manera simultánea en un cauce, con los
resultados de una operación pasados a la siguiente sin la
necesidad de almacenarlos en relaciones temporales.

  • Materialización.

Este enfoque de implementación toma la
expresión y la representa en una estructura anexa
(comúnmente un árbol de operadores). Luego se
comienza por las operaciones de más bajo nivel, las
entradas a estas operaciones son las relaciones de la base de
datos, estas operaciones se ejecutan utilizando los algoritmos ya
estudiados y almacenando sus resultados en relaciones temporales.
Luego se utilizan estas relaciones temporales para ejecutar las
operaciones del siguiente nivel en el árbol.

Una evaluación como la descrita se llama evaluación
materializada, puesto que los resultados de cada operación
intermedia se crean (materializan) con el fin de ser utilizados
en la evaluación de las operaciones del siguiente
nivel.

El costo de una
evaluación materializada no es simplemente la suma de los
costos de las
operaciones involucradas. Dado que los costos estimados de los
algoritmos presentados anteriormente no consideran el resultado
de la operación en disco, por lo tanto, al costo de las
operaciones involucradas hay que añadir el costo de
escribir los resultados intermedios en disco. Suponiendo que los
registros del
resultado se almacenan en una memoria intermedia y que cuando
esta se llena, los registros se escriben en el disco. El costo de
copiar los resultados se puede estimar en donde es el número
aproximado de tuplas de la relación resultado y es el factor de bloqueo
de la relación resultado.

  • Encauzamiento.

Se puede mejorar la evaluación de una consulta
mediante la reducción del número de archivos
temporales que se producen. Por ejemplo, considérese el
join de dos relaciones seguida de una proyección. Si se
aplicara materialización en la evaluación de esta
expresión implicaría la creación de una
relación temporal para guardar el resultado del join y la
posterior lectura de esta para realizar la proyección.
Estas operaciones se pueden combinar como sigue. Cuando la
operación de join genera una tupla del resultado, esta se
pasa inmediatamente al operador de proyección para su
procesamiento. Mediante la combinación del join y de la
proyección, se evita la creación de resultados
intermedios, creando en su lugar el resultado final
directamente.

La implementación del encauzamiento se puede
realizar de dos formas:

  1. Bajo demanda
    (enfoque top-down)
  2. Desde los procedimientos
    (enfoque bottom-up)

En un encauzamiento bajo demanda el sistema reitera
peticiones de tuplas desde la operación de la cima del
encauzamiento. Cada vez que un operador recibe una
petición de tuplas calcula la siguiente tupla a devolver y
la envía al procesador de
consultas. En un encauzamiento desde los procedimientos, los
operadores no esperan a que se produzcan peticiones para producir
las tuplas, en su lugar generan las tuplas impacientemente. Cada
operación del fondo del encauzamiento genera continuamente
tuplas de salida y las coloca en las memorias
intermedias de salida hasta que se llenan. Así, cuando un
operador en cualquier nivel del encauzamiento obtiene sus tuplas
de entrada de un nivel inferior del encauzamiento, produce las
tuplas de salida hasta llenar su memoria intermedia de
salida.
El sistema necesita cambiar de una operación a otra
solamente cuando se llena una memoria intermedia de salida o
cuando una memoria intermedia de entrada está vacía
y se necesitan más tuplas de entrada para generar las
tuplas de salida. Las operaciones de encauzamiento se pueden
ejecutar concurrentemente en distintos procesadores.

El encauzamiento bajo demanda se utiliza
comúnmente más que el encauzamiento desde los
procedimientos dada su facilidad de
implementación.

  1. Algoritmos de encauzamiento.

Supóngase un join cuya entrada del lado izquierdo
esta encauzada, dado que esta entrada no está
completamente disponible, implica la imposibilidad e utilizar un
join por mezcla (dado que no se sabe si la esta entrada viene o
no ordenada). El ordenar la relación significa transformar
el procedimiento en materialización. Este ejemplo ilustra
que la elección respecto al algoritmo a utilizar para una
operación y las elecciones respecto del encauzamiento son
dependientes una de la otra.
El uso eficiente del encauzamiento necesita la utilización
de algoritmos de evaluación que puedan generar tuplas de
salida según se están recibiendo tuplas por la
entrada de la operación. Se pueden distinguir dos
casos:

  1. Solamente una de las entradas está
    encauzada.
  2. Las dos entradas de un join están
    encauzadas.

Si únicamente una de las entradas está
encauzada, un join en bucle anidado indexado es la
elección más natural, ahora bien, si se sabe de
antemano que las tuplas de la entrada encauzada están
ordenadas por los atributos de join y la condición de join
es un equi-join también se puede usar un join por mezcla.
Se puede utilizar un join por asociación híbrida
con la entrada encauzada como la relación para probar
(relación r). Sin embargo, las tuplas que no
están en la primera partición se enviarán a
la salida solamente después de que la relación de
entrada encauzada se reciba por completo. Un join por
asociación híbrida es útil si la entrada no
encauzada cabe completamente en memoria, o si al menos la
mayoría de las entradas caben en memoria.

Si ambas entradas están encauzadas, la
elección de los algoritmos de join se limita. Si ambas
entradas están ordenadas por el atributo de join y la
condición de join es un equi-join entonces se puede
utilizar el método de join por mezcla. Otra técnica
alternativa es el join por encauzamiento que se presenta a
continuación. El algoritmo supone que las tuplas de
entrada de ambas relaciones r y s están
encauzadas. Las tuplas disponibles de ambas relaciones se dejan
listas para su procesamiento en una estructura de cola simple.
Asimismo se generan marcas especiales
llamadas y
, que sirven
como marcas de fin de archivo y que se insertan en la cola
después de que se hayan generado todas las tuplas de
r y de s (respectivamente). Para una
evaluación eficaz, se deberían construir los
índices apropiados en las relaciones r y s.
Según se añaden las tuplas a ambas relaciones se
deben mantener los índices actualizados.

El algoritmo de join encauzado es el
siguiente:

hechor = falso;
hechos = falso;
r = ;
s = ;
resultado = ;
mientras not hechor or not
hechos
si la cola está vacía entonces esperar
hasta que la cola no este vacía;

t = primera entrada de la cola;

si t = Finr entonces
hechor = verdadero;
sino
si t = Fins entonces
hechos = verdadero;
sino
si t es de la entrada r entonces
r = r {t};
resultado = resultado ({t} s);
sino
s = s {t};
resultado = resultado (r {t});
fin si
fin si
fin si
fin mientras

  1. Hasta ahora se han estudiado algoritmos para
    evaluar extensiones de operaciones del álgebra relacional y se han estimado
    sus costos. Dado que una consulta se puede evaluar de
    distintas maneras y por lo tanto con distintos costos
    estimados, este apartado considerará formas
    alternativas y equivalentes a sus expresiones.

    1. Cada implementación de base de
      datos tiene su forma de representación
      interna de consultas independientes del lenguaje de
      consultas utilizado. La representación interna
      debe cumplir con la característica de ser
      relacionalmente completo, es por eso que
      comúnmente los motores de BD eligen la
      representación del álgebra relacional en
      forma de árbol sintáctico abstracto para
      su representación interna.
      Dada una expresión del álgebra
      relacional, es trabajo del optimizador alcanzar un
      plan
      de evaluación que calcule el mismo resultado que
      la expresión dada pero de la manera menos
      costosa de generar. Para encontrar este plan de
      evaluación el optimizador necesita generar
      planes alternativos que produzcan el mismo resultado
      que la expresión dada y elegir el más
      económico de ellos. Para implementar este paso
      el optimizador debe generar expresiones que sean
      equivalentes a la expresión dada por medio del
      uso de las reglas de equivalencia que se explican a
      continuación.

    2. Equivalencia de expresiones.
    3. Reglas de equivalencia.
  2. Transformación de expresiones
    relacionales.

Una regla de equivalencia
dice que las expresiones de dos formas son equivalentes, por lo
tanto se puede transformar una en la otra mientras se preserva la
equivalencia. Se entiende como preservar la equivalencia al hecho
de que las relaciones generadas por ambas expresiones tienen el
mismo conjunto de atributos y contienen el mismo conjunto de
tuplas.

Formalmente se dice que se representa una
expresión en su forma canónica.

La noción de forma canónica es central a
muchos brazos de la matemática y otras disciplinas
relacionadas. Esta puede ser definida como sigue:

Dado un conjunto de Q objetos (digamos consultas) y una
noción de equivalencias entre objetos (digamos, la
noción de que q1 y q2 son equivalentes si y sólo si
ellas producen el mismo resultado), un subconjunto C de Q se dice
la forma canónica de Q (bajo la definición de
equivalencia expuesta anteriormente) si y sólo si cada
objeto q en Q es equivalente a sólo un objeto c en C. El
objeto c es llamado la forma canónica de el objeto q.
Todas las propiedades de interés
que se aplican al objeto q también se aplican a su forma
canónica c; por lo tanto es suficiente estudiar
sólo el pequeño conjunto de formas canónicas
C y no el conjunto Q con el fin de probar una variedad de
resultados.
Las reglas de equivalencia para llevar la expresión
relacional a una equivalente son:

 

  1. Cascada de proyecciones:

  1. Cascada de selecciones:

  1. Conmutación de
    selecciones:

  1. Conmutación de selección y
    proyección.

  1. Conmutación del Join.

  1. Asociatividad del Join Natural.
    caso1.

caso 2. Involucra sólo atributos de y .

  1. Distributividad de la selección con
    respecto al join.

Caso 1. Involucra sólo atributos de .

Caso 2. involucra sólo atributos de y involucra
sólo atributos de

 

  1. Distributividad de la proyección con
    respecto al join.

Si y son los atributos de y respectivamente.

 

  1. Conmutatividad de la unión y la
    intersección.

La diferencia de conjuntos no es
conmutativa.

 

  1. Asociatividad de la unión e
    intersección.

 

  1. Distributividad de la selección con
    respecto a la unión, intersección y
    diferencia.

 

  1. Distributividad de la proyección con
    respecto a la unión.

 

Ejemplo:
Supóngase que lo que se desea es notificar a todos los
dueños de vehículos del año, que
estén siendo conducidos por los chóferes con menos
experiencia (supóngase un año o menos); la
expresión relacional sería:

Se puede utilizar la regla 6.1 con el fin de asociar el
Join.

Aplicando la regla 5 se puede conmutar el Join.

Luego se aplica la regla 7.1 con el fin de distribuir la
selección sobre el join.

Aplicando la regla 7.2 se obtiene:

Las siguientes figuras muestran la expresión inicial y la
final en una estructura de árbol
sintáctico.

Así, los optimizadores generan de manera
sistemática expresiones equivalentes a la consulta dada.
El proceso se entiende como sigue: dada una expresión, se
analiza cada subexpresión para saber si se puede aplicar
una regla de equivalencia. De ser así se genera una nueva
expresión donde la subexpresión que concuerda con
una regla de equivalencia se reemplaza por su equivalente. Este
proceso continúa hasta que no se pueden generar más
expresiones nuevas. Una optimización en términos de
espacio se puede lograr como sigue. Si se genera una
expresión E1 de una expresión E2 mediante una regla
de equivalencia, entonces E1 y E2 son equivalentes en su
estructura y por lo tanto sus subexpresiones son
idénticas. Las técnicas
de representación de expresiones que permiten a ambas
expresiones apuntar a la subexpresión compartida pueden
reducir el espacio de búsqueda significativamente.

5.- Estructuras de
control

  1. Estructuras de control

En lenguajes de programación, las estructuras
de control
permiten modificar el flujo de ejecución de
las instrucciones de un programa.

Con las estructuras de control se
puede:

  • De acuerdo a una condición, ejecutar un
    grupo u otro
    de sentencias (If-Then-Else y Select-Case)
  • Ejecutar un grupo de sentencias mientras
    exista una condición (Do-While)
  • Ejecutar un grupo de sentencias hasta que
    exista una condición (Do-Until)
  • Ejecutar un grupo de sentencias un número
    determinado de veces (For-Next)

Todas las estructuras de control tienen un único
punto de entrada y un único punto de salida. Según
fuchi, las estructuras de control se puede clasificar en:
secuenciales, iterativas y de control avanzadas, by fuchi ing, de
sistemas 6 sem Esto es una de las cosas que permite que la
programación se rija por los principios de
la programación estructurada.

Los lenguajes de programación modernos
tienen estructuras de control similares. Básicamente lo
que varía entre las estructuras de control de los
diferentes lenguajes es su sintaxis, cada lenguaje tiene una
sintaxis propia para expresar la estructura.

Otros lenguajes ofrecen estructuras diferentes, como por
ejemplo los comandos guardados.

  1. Algunas estructuras de control en el
    lenguaje BASIC y Visual
    Basic.

    1. If-Then-Else
    2. Si la
      condición es verdadera, se ejecuta el bloque de
      sentencias 1, de lo contrario, se ejecuta el bloque de
      sentencias 2.

      IF
      (Condición) THEN

      (Bloque de
      sentencias 1)

      ELSE

      (Bloque se
      sentencias 2)

      END
      IF

    3. Select-Case
  2. Tipos de estructura de
    control
  • Se evalúa la expresión, dando como
    resultado un número.
  • Luego, se recorren los "Case" dentro de la estructura
    buscando que el número coincida con uno de los
    valores.
  • Es necesario que coincidan todos sus
    valores.
  • Cuando se encuentra la primera coincidencia, se
    ejecuta el bloque de sentencias correspondiente y se sale de la
    estructura Select-Case.
  • Si no se encuentra ninguna coincidencia con
    ningún valor, se ejecuta el bloque de sentencias de la
    sección "Case Else".

Select (Expresión)

Case Valor1

(Bloque de sentencias 1)

Case Valor2

(Bloque de sentencias 2)

Case Valor n

(Bloque de sentencias 3)

Case Else

(Bloque de sentencias "Else")

End Select

  1. Mientras la condición sea verdadera,
    se ejecutarán las sentencias del
    bloque.

    Do While
    (Condición)

    (Bloque de sentencias)

    Loop

  2. Do-While

    Se ejecuta el bloque de sentencias, hasta que la
    condición sea verdadera

    Do

    (Bloque de sentencias)

    Loop Until (Condición)

  3. Do-Until
  4. For-Next
  • Primero, se evalúan las expresiones 1 y
    2, dando como resultado dos números.
  • La variable del bucle recorrerá los
    valores desde el número dado por la expresión 1
    hasta el número dado por la expresión
    2.
  • El bloque de sentencias se ejecutará en
    cada uno de los valores que tome la variable del
    bucle.

For (Variable) = (Expresión1) To
(Expresión2)

(Bloque de sentencias)

Next

  1. Las estructuras de control básicas
    pueden anidarse, es decir pueden ponerse una dentro de
    otra.

    1. IF A > B THEN

      For X = 1 To 5

      (Bloque de sentencias 1)

      Next

      Else

      (Bloque de instrucciones 2)

      End If

    2. Estructura For-Next dentro de una
      estructura If-Then-Else
    3. Estructura If-Then-Else dentro de
      estructura For-Next

      If A = C Then

      GHDGDVDVVDWVD

      Else

      EEFFSGWSGGSW

      SNSBS

      End If

      Next

    4. For SKWSx = 10 To
      20
    5. Estructura For-Next que está
      dentro de estructura Do-While
  2. Estructuras
    anidadas

Do While A > 0

For X = 1 to 10

(Bloque de instrucciones)

Next

A = A – 1

Loop

contador=0 for(i=1;i<=10;i++) do {

adrian=adrian+contador

}

Estructura If-Then dentro de estructura For-Next dentro
de estructura Do-While ===

Do While A > 0

For X = 1 to 10

If A = C Then

(Bloque de instrucciones1)

Else

(Bloque de instrucciones2)

End If

Next

Partes: 1, 2, 3
 Página anterior Volver al principio del trabajoPágina siguiente 

Nota al lector: es posible que esta página no contenga todos los componentes del trabajo original (pies de página, avanzadas formulas matemáticas, esquemas o tablas complejas, etc.). Recuerde que para ver el trabajo en su versión original completa, puede descargarlo desde el menú superior.

Todos los documentos disponibles en este sitio expresan los puntos de vista de sus respectivos autores y no de Monografias.com. El objetivo de Monografias.com es poner el conocimiento a disposición de toda su comunidad. Queda bajo la responsabilidad de cada lector el eventual uso que se le de a esta información. Asimismo, es obligatoria la cita del autor del contenido y de Monografias.com como fuentes de información.

Categorias
Newsletter