Portafolio de Evidencias Intérpretes.
Universidad del Desarrollo Profesional. Unidad Centro.
Ingeniería en Sistemas Computacionales. Elaboro: Víctor Hugo Ibarra Ortiz. Matricula: 25113172.
Maestro: Dr. José Benito Franco Urrea. Materia: Interpretes. Cd, Obregón, Sonora a 07 de Octubre de 2013.
INDICE. •
INFORMACIÓN ESCOLAR.
•
INTRODUCCIÓN.
•
DEFINICIÓN DE INTÉRPRETE.
•
ESTRUCTURA DE UN INTÉRPRETE.
•
VENTAJAS Y DESVENTAJAS DE LOS INTÉRPRETES.
•
APLICACIÓN DE LOS SISTEMAS BASADOS EN INTÉRPRETES.
•
TIPOS DE INTÉRPRETES.
•
INTRODUCCIÓN A LOS ANÁLISIS SINTÁCTICOS.
•
TAREAS. 1. INVESTIGACIÓN DE CONCEPTOS. DEPURACIÓN, ERRORES SINTÁCTICO, ERRORES SEMÁNTICOS, ERRORES EN TIEMPO DE EJECUCIÓN. 2. PROGRAMACION IMPERATIVA, FUNCIONAL, ASPECTO, ARBOLES SINTACTICOS. 3. MATRIZ DE TRANSICIÓN DE ESTADO.
•
PRÁCTICAS DE CLASE MODO CONSOLA EN PYTHON.
•
RESOLUCIÓN DE EJERCICIOS EN SHARPDEVELOP.
•
CONCLUSION.
INFORMACION INSTITUCIONAL. MISION. La misión de UNIDEP es formar profesionales de éxito que cuenten con actitudes, habilidades y conocimientos que demanda el sector productivo de la región.
VISION. La Universidad del Desarrollo Profesional es una institución de educación superior de calidad, que ofrece programas presénciales y semipresenciales de bachillerato, profesional asociado, licenciatura, postgrado, diplomados y cursos en México y en el extranjero. Se distingue por facilitar a sus egresados la incorporación al mercado de trabajo, apoyada en una estrecha vinculación con el sector productivo y en planes de estudios pertinentes y dinámicos. Es reconocida por su modelo educativo profesionalizante, por la flexibilidad de su oferta académica impartida en ciclos continuos y por horarios y cuotas accesibles, acordes a la disponibilidad de tiempo y recursos económicos del alumno. Cuenta con profesores de amplia experiencia profesional y educativa. Sus instalaciones dentro de la ciudad permiten el fácil acceso. Cuenta con un modelo de administración sistematizado, participativo, operado por personal que es recompensado por su desempeño efectivo que le permite maximizar las aportaciones de sus socios y mantener finanzas sanas.
VALORES Y ACTITUDES UNIDEP. Lealtad._ Los Integrantes de la comunidad Universitaria consideramos la fidelidad como un valor excelso que enaltecemos en nuestro quehacer diario. Justicia._ Los integrantes de la comunidad Universitaria actuamos con la constante y perpetua voluntad de dar a cada cual lo que le corresponde conforme a sus méritos o actos. Honestidad._ Los integrantes de la comunidad universitaria actuamos con sinceridad y honradez en nuestras tareas y en congruencia entre los pensamientos, palabras y acciones.
Responsabilidad._ Los integrantes de la comunidad universitaria llevamos a cabo nuestras actividades con integridad, con sentido del propósito y apegados a los objetivos institucionales. Esfuerzo._ Los integrantes de la comunidad universitaria usamos nuestra máxima energía para cumplir con los objetivos trazados. Creatividad._ Los integrantes de la comunidad universitaria resolvemos los problemas con imaginación, conocimientos y con un espíritu de mejora continua.
INTRODUCCION. En el siguiente trabajo se incluyó la información institucional y los valores y actitudes de la institución en la que estoy actualmente estudiando. También la definición de Intérprete, cuál es su estructura, al igual que sus ventajas y desventajas y los sistemas basados en intérpretes. Los tipos de intérpretes, así como una introducción a los análisis sintácticos y las asignaciones hechas en el módulo. Al igual que se incluyeron las prácticas en Phyton y Sharpdevelop y una conclusión y referencias.
DEFINICIÓN DE INTÉRPRETE. Un interpretador es un programa informático capaz de analizar y ejecutar otros programas, escritos en un lenguaje de alto nivel. Los intérpretes se diferencian de los compiladores en que mientras estos traducen un programa desde su descripción en un lenguaje de programación al código de máquina del sistema, los intérpretes sólo realizan la traducción a medida que sea necesaria, típicamente, instrucción por instrucción, y normalmente no guardan el resultado de dicha traducción.
Interpretar: ejecutar un programa escrito en un lenguaje de alto nivel traduciéndolo línea por línea. Compilar: traducir un programa escrito en un lenguaje de alto nivel a un lenguaje de bajo nivel de una vez, en preparación para la ejecución posterior.
Los intérpretes son a menudo usados para construir máquinas virtuales que enlazan la máquina de computación esperada por la semántica y la máquina de computación disponible en el hardware.
Lenguaje C++
ESTRUCTURA DE UN INTÉRPRETE. A la hora de construir un intérprete es conveniente utilizar una Representación Interna (RI) del lenguaje fuente a analizar. De esta forma, la organización interna de la mayoría de los intérpretes se descompone en los módulos: 1. Traductor a Representación Interna: Toma como entrada el código del programa P en Lenguaje Fuente, lo analiza y lo transforma a la representación interna correspondiente a dicho programa P. 2. Representación Interna (P/RI): La representación interna debe ser consistente con el programa original. Entre los tipos de representación interna, los árboles sintácticos son los más utilizados y, si las características del lenguaje lo permiten, pueden utilizarse estructuras de pila para una mayor eficiencia. 3. Tabla de símbolos: Durante el proceso de traducción, es conveniente ir creando una tabla con información relativa a los símbolos que aparecen. La información a almacenar en dicha tabla de símbolos depende de la complejidad del lenguaje fuente. Se pueden almacenar etiquetas para instrucciones de salto, información sobre identificadores (nombre, tipo, línea en la que aparecen, etc.) o cualquier otro tipo de información que se necesite en la etapa de evaluación. 4. Evaluador de Representación Interna: A partir de la Representación Interna anterior y de los datos de entrada, se llevan a cabo las acciones
indicadas para obtener los resultados. Durante el proceso de evaluación es necesario contemplar la aparición de errores. 5. Tratamiento de errores: Durante el proceso de evaluación pueden aparecer diversos errores como desbordamiento de la pila, divisiones por cero, etc. que el intérprete debe contemplar.
VENTAJAS Y DESVENTAJAS DE LOS INTÉRPRETES. Los intérpretes realizan normalmente dos operaciones: Traducen el código fuente a un formato interno (esta operación no es estrictamente indispensable). Ejecutan o interpretan el programa traducido al formato interno. Ventajas de un intérprete. Flexibilidad: permite realizar acciones complejas, imposibles o muy difíciles con un compilador, como las siguientes: Ejecución de cadenas de caracteres mediante operadores como "execute", "interprete" o "evalquote". Modificar sobre la marcha el significado de los símbolos e incluso prescindir por completo de las declaraciones. Obtener un ligamiento dinámico completo en los sistemas orientados a objetos. Simplificar la gestión de memoria en los programas fuente. Facilidad de depuración de programas: la interpretación puede interrumpirse en cualquier momento para examinar o modificar los valores de las variables o la situación en la ejecución. La tabla de símbolos está disponible. Se pueden corregir
los errores y continuar. Trazas y paradas programadas. Saltos en el programa. Abandonos de subrutinas. Rapidez en el desarrollo. Desventajas de un intérprete. Velocidad: usualmente un orden de magnitud menor que la de un programa compilado. Tamaño del programa objeto, que exige añadir el intérprete al programa propiamente dicho. APLICACIÓN DE LOS SISTEMAS BASADOS EN INTÉRPRETES. Los sistemas interpretados han tenido una gran importancia desde la aparición de los primeros ordenadores. En la actualidad, la evolución del hardware abre nuevas posibilidades a los sistemas interpretados. La preocupación ya no es tanto la eficiencia como la capacidad de desarrollo rápido de nuevas aplicaciones. Las principales aplicaciones podrían resumirse en: Intérpretes de Comandos: Los sistemas operativos cuentan con intérpretes de comandos como el Korn-Shell, C-Shell, JCL, etc. Estos intérpretes toman un lenguaje fuente que puede incluir sentencias de control (bucles, condiciones, asignaciones, etc.) y ejecutan los diferentes comandos a medida que aparecen en el lenguaje. Lenguajes basados en Escritos (Scripting Languages), diseñados como herramientas que sirvan de enlace entre diferentes sistemas o aplicaciones. Suelen ser interpretados con el fin de admitir una mayor flexibilidad a la hora de afrontar las peculiaridades de cada sistema. Podrían destacarse Perl, Tcl/Tk, JavaScript, WordBasic [Ousterhout 97] Entornos de Programación: Existen ciertos lenguajes que contienen características que impiden su compilación o cuya compilación no es efectiva. Estos lenguajes suelen disponer de un Complejo entorno de desarrollo interactivo con facilidades para la depuración de programas. Entre estos sistemas pueden destacarse los entornos de desarrollo para Lisp, Visual Basic, Smalltalk, etc. Lenguajes de Propósito Específico: Ciertos lenguajes incluyen sentencias que realizan tareas complejas en contextos específicos. Existe una gran variedad de aplicaciones en las que se utilizan este tipo de lenguajes como consultas de Bases de Datos, simulación, descripción de hardware, robótica, CAD/CAM, música, etc.
Sistemas en Tiempo Real: Entornos que permiten modificar el código de una aplicación en tiempo de ejecución de forma interactiva. Intérprete de Código Intermedio: Una tendencia tradicional en el diseño de compiladores es la generación de un código intermedio para una máquina abstracta, por ejemplo, el P-Code de Pascal o los bytecodes de Java. El siguiente paso puede ser: generación del código objeto a partir del código intermedio para una máquina concreta, finalizando el proceso de compilación o interpretar dicho código intermedio en una máquina concreta. La tendencia habitual es definir un lenguaje intermedio independiente de una máquina concreta. Para ello, suele definirse una máquina virtual que contenga las instrucciones definidas por el lenguaje intermedio, permitiendo una mayor portabilidad. Un ejemplo sería la Máquina Virtual de Java, que es simulada en la mayoría de los visualizadores Web. TIPOS DE INTÉRPRETES. En función de la estructura interna del intérprete se pueden distinguir:
Intérpretes puros. Intérpretes avanzados. Intérpretes incrementales. Evaluadores parciales. Compiladores “Just in time.” Compilación continúa.
Intérpretes Puros: Analizan y ejecutan todo programa fuente sentencia a sentencia.
Pensados pata maquinas con limitación de memoria. Si la mitad del programa se producen errores entonces hay que volver a ejecutar desde el inicio. Todas las instrucciones en RI están etiquetadas o consecutivas en una estructura fija (array). El evaluador de instrucciones determina la instrucción que debe ejecutarse y los datos de entrada que necesita o emite un error.
Intérpretes Avanzados: Avanzados o normales.
Incorporan un paso previo de análisis de todo el programa fuente. Genera posteriormente un lenguaje RI ejecutado por el intérprete.
Se usa para lenguajes más avanzados, ya que permite otro tipo de análisis y optimizaciones.
Intérpretes Incrementales. Dirigidos a lenguajes que no pueden compilarse.
Pueden crear objetos o funciones en tiempo de ejecución (Lisp, Prolog, Smalltalk). Se compilan las partes estáticas. Posteriormente, en tiempo de ejecución se compilan las partes dinámicas requeridas (o recopilan las modificaciones). No producen código objeto independiente, vienen acompañados de módulos que en tiempo de ejecución permiten la compilación (run time system).
Evaluadores Parciales (epecializadores).
Programas con dos tipos de datos de entrada. Se gana eficiencia cuando es conocido que un programa va a ejecutarse muchas veces sobre un mismo conjunto de datos. Binding-time analysis es una técnica para determinar los datos estáticos. Binding time analysis => Análisis del tiempo de unión.
Compiladores “Just in time”
Surgen con la necesidad de distribuir programas independientes de la máquina. Para evitar la interpretación los códigos de bytes se transforman a código nativo. El código es compilado a código nativo justo en el momento que lo necesita el programa.
Compilación Continua.
Mejora de “Just in time”. Mezcla compilación a código nativo con la interpretación. Dispone de dos módulos: Un intérprete de código de bytes. Un compilador de código de bytes a código nativo. Deberían actuar a la vez. El código es mezcla de código fuente y nativo. Al principio está todo sin compilar, a medida que el programa es ejecutado al compilador va generando traducciones a código nativo. Compilador. Traduce las unidades de compilación a código nativo. A medida que se finaliza la traducción de una unidad, la versión en código nativo se deja disponible al intérprete. Interprete. Se responsabiliza de la ejecución del programa. Comienza interpretando el código fuente, haciendo saltos a las versiones en código nativo a medida que estas están disponibles. Monitor. Coordina la comunicación entre los dos módulos anteriores.
INTRODUCCIÓN A LOS ANÁLISIS SINTÁCTICOS. También conocido como Análisis Sintáctico por Desplazamiento y Reducción; sus principales operaciones son como su nombre lo dice, desplazar y reducir. Aunque existen en realidad cuatro acciones posibles que puede realizar un analizador LR: desplazar, reducir, aceptar y error.
El análisis sintáctico LR, tiene un conjunto más amplio de gramáticas que el análisis sintáctico LL, además dichas gramáticas son un supra conjunto de los clases de gramáticas que se pueden analizar con analizadores sintáctico predictivos y se pueden generar expresiones más sencillas.
También debido a su sencillez y a su capacidad de análisis para una gran variedad de lenguajes, la técnica de análisis LR es elegida para los generadores automáticos de analizadores sintácticos; ya que con ellos se puede reconocer prácticamente todas las construcciones de los lenguajes de programación y así escribir gramáticas independientes de contexto.
Debido a estas razones y a que el desarrollo de la tabla y la aplicación del algoritmo fueron sencillo, opte por elegir este método de LR sobre el de LL, que considere más complicado en su desarrollo.
ANALISIS SINTACTICO LR Los códigos de las acciones son: 1. Di significa desplazar y meter en la pila el estado i, 2. Rj significa reducir por la producción con número j, 3. Ok significa aceptar, 4. El espacio en blanco significa error. apuntar a al primer símbolo de w$; repeat forever begin sea s el estado en la cima de la pila y a el símbolo apuntado por ae; if acción [s,a] = desplazar s then begin meter a y después s en la cima de la pila; avanzar ae al siguiente símbolo de entrada end else if acción[s,a] = reducir A then begin
sacar 2*símbolos de la pila; sea s el estado que ahora está en la cima de la pila; meter A y después ir_a [s,A] en la cima de la pila; emitir la producción A end else if acción[s,a] = aceptar then return else error( ) end GRAMATICA DEL LENGUAJE P D Inicio C Fin D (nume dato) id V V ,id ; CILAS A id = id ( id const E ) EE+TE-TT TT*FT/FF F ( E ) id const S S && G S || G | G G G == O G != O | O O O < P O P O >= P P P !Q | Q Q ( S ) id const Z ( X ) id const S Si ( X Y ) { C W } R W C R Si_no { C } I Impr( id ) Impr(“mensaje”) Impr(“mensaje”, id) L Leer( id ) PARA LA TABLA DE OPERADORES ARITMETICOS I: E’ E EE+T EE-T ET TT*F
TT/F TF F(E) F id F const Ir_a ( I , E ) Ir_a ( I , T ) Ir_a ( I , F ) I0: I1: I2: I3: E’ . E E’ E. E T. T F. E . E + T E E. + T T T. * F E . E - T E E. - T T T. / F E . T T . T * F T . T / F T.F F.(E) F . id F . const Ir_a ( I , ( ) Ir_a ( I , id ) Ir_a ( I , const ) Ir_a ( I , + ) I4: I5: I6: I7: F (. E ) F id . F const . E E + . T E . E + T E . E + T T . T * F E . E – T T . T / F E . T T . F T . T * F F . ( E ) T . T / F F . id T . F F . const F . ( E ) F . id F . const Ir_a ( I , - ) Ir_a ( I , * ) Ir_a ( I , / ) Ir_a ( I , E ) I8: I9: I10: I11: E E - . T T T *. F T T /. F F ( E . ) T . T * F F . ( E ) F . ( E ) E E . + T T . T / F F . id F . id E E . - T T.FF
TAREAS. QUE ES LA DEPURACION (debugging)? Depurar consiste en eliminar impurezas, pero en jerga informática es un vocablo utilizado en el trabajo de programación, que consiste en revisar y analizar si la sintaxis de un programa creado es correcta y/o genera errores al ejecutarlo. En inglés se denomina a este proceso debugging, esto es, eliminar bugs o errores (bug = bicho en inglés). Mediante la depuración lo que se hace es ejecutar el programa paso a paso, y si alguna instrucción no es correcta o no la comprende el ordenador, entonces se genera un informe de error para esa instrucción en particular, permitiendo así que el programador la corrija posteriormente. El debugging o depuración es algo imprescindible para cualquier programador. Se use el lenguaje de programación que se use, lo primero que se hace al programar es escribir el código fuente, que es la forma entendible para el programador. Cualquier error en ese código fuente implicará un malfuncionamiento en el programa, por lo que se hace necesario localizarlo y corregirlo. Eso se lleva a cabo con el debug. Tras la depuración el programador podrá compilarlo, esto es, traducirlo a un lenguaje comprensible para el ordenador (aunque no para las personas). Y una vez compilado el programador no podrá retocarlo, sino que si quiere modificarlo deberá volver al código fuente, cambiarlo, revisarlo de nuevo y compilarlo. Por eso es tan importante la compilación, puesto que el proceso de subsanar errores es muy laborioso, máxime si los errores se dan cuando el programa ya ha sido distribuido, porque implicaría tener que cambiar el programa a todos los usuarios (algo que se suele hacer con las actualizaciones). Incluso después de la depuración, el programa optimizado resultante no siempre es estable al 100%, y suele requerir ser testeado y probado en profundidad por un equipo humano. Tras ello, se acostumbran a sacar versiones de prueba o beta a fin de que los usuarios la comprueben y testen los posibles fallos que encuentren, y modificarlo a posteriori para solucionarlos. El proceso de encontrar los fallos puede a veces ser más laborioso que la propia redacción del programa. Si bien es cierto que los errores de escritura suelen ser detectados fácilmente por los propios lenguajes de programación, no así los errores de funcionamiento del programa, los llamados errores lógicos. Por decirle claramente, una instrucción puede estar bien escrita, pero no ser lógica y por tanto llevar al programa a un malfuncionamiento, que se quede atrapado en un bucle sin fin… etc… solo un exhaustivo análisis y una prueba de todas las opciones disponibles, de las posibles incompatibilidades con otros programas, de probarlo en el ‘mundo real’, por así decirlo puede a veces dar con la solución. De ahí las
betas que comentábamos, y aun así, casi todos los programas suelen salir al mercado con errores o bugs conocidos. También, con otro significado, se habla de depuración de un sistema informático cuando limpiamos este de software maligno, como malware, virus, y lo optimizamos. Tipos de depuradores Existen muchos depuradores de Java disponibles: Ambientes integrados de desarrollo (Integrated Development Environments o IDE), tales como Eclipse, Sun ONE Studio, Borland JBuilder y BlueJ, contienen sus propios depuradores. Depuradores gráficos autónomos, tales como JSwat y DDD Depuradores basados en texto, tales como Sun jdb. Conclusión: Un depurador es una herramienta poderosa para monitorear la ejecución de un programa e identificar la fuente de errores. Sin embargo, un depurador es solamente una herramienta. La habilidad para identificar y eliminar errores depende, en última instancia, de la competencia del programador. QUE SON LOS ERRORES SINTÁCTICOS? Un error de sintaxis se detecta cuando el analizador sintáctico espera un símbolo que no corresponde al que se acaba de leer. Los analizadores sintácticos LL y LR tienen la ventaja de que pueden detectar errores sintácticos lo más pronto posible, es decir, se genera un mensaje de error en cuanto el símbolo analizado no sigue la secuencia de los símbolos analizados hasta ese momento. Manejo de Errores en el Análisis Sintáctico El analizador sintáctico detecta un error de sintaxis cuando el analizador léxico proporciona el siguiente símbolo y éste es incompatible con el estado actual del analizador sintáctico. Los errores sintácticos típicos son: Paréntesis o corchetes omitidos, por ejemplo, x : = y * (1 + z; Operadores u operando omitidos, por ejemplo, x : = y (1 + z ); Delimitadores omitidos, por ejemplo, x : = y + 1 IF a THEN y : = z. No hay estrategias de recuperación de errores cuya validez sea general, y la mayoría de las estrategias conocidas son heurísticas, ya que se basan en suposiciones acerca de cómo pueden ocurrir los errores y lo que probablemente
quiso decir el programador con una determinada construcción. Sin embargo, hay algunas estrategias que gozan de amplia aceptación: 1. Recuperación de emergencia (o en modo pánico): Al detectar un error, el analizador sintáctico salta todos los símbolos de entrada hasta encontrar un símbolo que pertenezca a un conjunto previamente definido de símbolos de sincronización. Estos símbolos de sincronización son el punto y como, el símbolo end o cualquier palabra clave que pueda ser el inicio de una proposición nueva. 2. Recuperación por inserción, borrado y reemplazo: éste también es un método fácil de implantar y funciona bien en ciertos casos de error. 3. Recuperación por expansión de gramática: El 60% de los errores en los programas fuente son errores de puntuación, por ejemplo, la escritura de un punto y coma en lugar de una coma, o viceversa. Una forma de recuperarse de estos errores es legalizarlos en ciertos casos, introduciendo lo que llamaremos producciones de error en la gramática del lenguaje de programación. La expansión de la gramática con estas producciones no quiere decir que ciertos errores no serán detectados, ya que pueden incluirse acciones para informar de su detección. La recuperación de emergencia es la estrategia que se encontrará en la mayoría de los compiladores, pero la legalización de ciertos errores mediante la definición de una gramática aumentada es una técnica que se emplea con frecuencia. No obstante, hay que expandir la gramática con mucho cuidado para asegurarse de que no cambien el tipo y las características de la gramática. Los errores de sintaxis se detectan cuando el analizador sintáctico espera un símbolo que no concuerda con el símbolo que está analizando, en los analizadores sintácticos LL, los errores de sintaxis se detectan cuando a y el no terminal que están en la cima de la pila nos llevan a un índice de una posición vacía de la tabla de análisis sintáctico. En los analizadores sintácticos LR, los errores de sintaxis se detectan cuando hay un índice a una posición vacía de la tabla, o sea, cuando no se especifica ninguna transición al analizar á en el estado actual. Sin embargo, si se emplea una gramática aumentada con producciones de error adicionales, no sólo se detectarán errores por medio de los índices a posiciones vacías de la tabla de análisis sintáctico. QUE SON LOS ERRORES EN TIEMPO DE EJECUCIÓN? (runtime error). Algunos programas de debugging sólo pueden ser llevados a cabo (o ser más eficientes o precisos) cuando se realizan en tiempo de ejecución. Por esta razón, algunos errores (bugs) de programación, no pueden ser descubiertos
hasta que el programa sea testeado en un entorno en "vivo" con datos reales, por más sofisticadas sean las pruebas que se le realicen previamente. Si se encuentra uno de estos errores, suele dispararse lo que se llama un mensaje de error en tiempo de ejecución. El manejo de excepciones, es una característica de algunos lenguajes de programación, que permiten manejar o controlar los errores en tiempo de ejecución. Proveen una forma estructurada de atrapar las situaciones completamente inesperadas, como también errores predecibles o resultados inusuales. Un error de ejecución típico ocurre cuando el índice de una matriz no es un elemento del subintervalo especificado o por intentar una división entre cero. En tales situaciones, se informa del error y se detiene la ejecución del programa. QUE SON LOS ERRORES SEMÁNTICOS? Los errores semánticos corresponden a la semántica del lenguaje de programación, la cual normalmente no está descrita por la gramática. Los errores semánticos más comunes son la omisión de declaraciones. Los errores que puede detectar el analizador sintáctico son aquellos que violan las reglas de una gramática independiente del contexto. Ya hemos mencionado que algunas de las características de un lenguaje de programación no pueden enunciarse con reglas independientes del contexto, ya que dependen de él; por ejemplo, la restricción de que los identificadores deben declararse previamente. Por lo tanto, los principales errores semánticos son: 1. Identificadores no definidos; 2. Operadores y operandos incompatibles. Es mucho más difícil introducir métodos formales para la recuperación de errores semánticos que para la recuperación de errores sintácticos, ya que a menudo la recuperación de errores semánticos es ad hoc. No obstante, puede requerirse que, por lo menos, el error semántico sea informado al programador, que se le ignore y que, por tanto, se suprimirá la generación de código. Sin embargo, la mayoría de los errores semánticos pueden ser detectados mediante la revisión de la tabla de símbolos, suponiendo un tipo que se base en el contexto donde ocurra o un tipo universal que permita al identificador ser un operando de cualquier operador del lenguaje. Al hacerlo, evitamos la producción de un mensaje de error cada vez que se use la variable no definida. Si el tipo de
un operando no concuerda con los requisitos de tipo del operador, también es conveniente reemplazar el operando con una variable ficticia de tipo universal. QUE SON LOS LENGUAJES FORMALES Y LENGUAJES NATURALES? Todos los lenguajes están constituidos por símbolos (es decir, signos artificiales), pero la formación del sistema de símbolos puede a su vez tener diversos grados de artificialidad. Los lenguajes artificiales se llaman así por la oposición al lenguaje natural. Entre los Lenguajes Artificiales suelen distinguirse el lenguaje técnico y el formal. 1) Lenguaje técnico. El lenguaje técnico podría denominarse, con mayor propiedad, “Lenguaje natural con términos técnicos”. Aparece cuando se otorga a determinados vocablos o expresiones un significado restringido, a través de definiciones precisas. Las ciencias utilizan comúnmente este procedimiento para otorgar mayor precisión al lenguaje, pero la base y la estructura del lenguaje técnico son las mismas del lenguaje natural del que se parte. Todos los profesionales hablan un mismo idioma, que conserva su vocabulario general y su construcción gramatical; pero cada uno agrega a esta base común términos estrictamente definidos que constituyen el lenguaje específico (o la jerga profesional) de cada ciencia, con un mayor grado de univocidad en los conceptos relevantes para determinado sector del conocimiento. Los términos definidos pueden haber sido tomados del lenguaje natural, o puede haberse creado un vocablo para nombrar una categoría hasta entonces inexistente en el lenguaje, y que el científico considera relevante. 2) Lenguaje formal. A veces, el uso de un lenguaje técnico no es suficiente: las palabras utilizadas pueden traer a la mente asociaciones no queridas, y la vaguedad (o a veces la ambigüedad) no puede en ocasiones reducirse aceptablemente. Un lenguaje formal es aquel en el que se han eliminado los términos del lenguaje ordinario y se emplean únicamente símbolos arbitrarios, de cuyo significado se prescinde para dirigir la atención exclusivamente sobre las relaciones entre dichos símbolos, expresadas en fórmulas, tal como en el álgebra o en la lógica formal. En álgebra, por ejemplo, decimos: (a+b) = (b+a) Esta expresión formal puede verse como un cálculo, sin referencia alguna a la realidad concreta, y en este sentido la comprendemos como la enunciación de cierta propiedad de la suma.
PROGRAMACION IMPERATIVA. La programación imperativa es la forma natural de programar un computador, es el estilo de programación que se utiliza en el ensamblador, el estilo más cercano a la arquitectura del computador. Características de la arquitectura clásica de Von Newmann: Memoria donde se almacenan los datos (referenciables por su dirección de memoria) y el programa. Unidad de control que ejecuta las instrucciones del programa (contador del programa). Los primeros lenguajes de programación (como el Fortran) son abstracciones del ensamblador y de esta arquitectura. Lenguajes más modernos como el BASIC o el C han continuado con esta idea. Características principales de la programación imperativa: Idea principal de la programación imperativa: la computación se realiza cambiando el estado del programa por medio de sentencias que definen pasos de ejecución del computador. Estado del programa modificable. Sentencias de control que definen pasos de ejecución. PROGRAMACION FUNCIONAL. En el paradigma de la programación funcional, un programa se considera una función matemática, la cual describe una relación entre una entrada y una salida y donde el concepto de estado o variable se elimina completamente. Sabemos que una función matemática es una regla que asocia a cada x de un conjunto X de valores, un único y de otro conjunto Y de valores; se representa por: f:X → Y o por y = f(x) Si ignoramos cómo un programa computa sus datos y nos fijamos sólo en qué computa, podemos ver al mismo como una función matemática que dada una entrada, devuelve una salida; se representa por Programa: Input -> Output u output = Programa (input). De aquí que la implementación de un lenguaje que permita crear programas con el enfoque de una función matemática, debía ser basado en el recurso función de los lenguajes de programación... Ahora bien, entre la concepción de una función matemática y la de una función en los lenguajes de programación tradicionales, existían diferencias: Lenguajes de programación: Hay distinción entre la definición de la función (descripción de lo que va a hacer la función mediante los parámetros formales) y la aplicación de la misma (llamada con los parámetros
actuales). Las variables refieren una zona de memoria donde se almacena un valor Matemáticas: La distinción no es clara, a menudo el término " variable independiente " se usa tanto para el parámetro formal como actual. Ejemplo: Si se tiene la expresión "Sea x tal que f(x) = 2 “, no se distingue con claridad si se refiere a la definición de la función constante de valor 2 o al punto x específico en el que una función f ya definida toma el valor 2. Las variables siempre se refieren a un valor y no a una localización de memoria. No hay concepto de localización de memoria y por tanto la expr. x=x+1 no tiene sentido. La programación funcional debe por esto eliminar el concepto de variable excepto como nombre de un valor. Por lo anterior, se concibió que en la Programación Funcional la asignación no fuera permitida como instrucción. En programación funcional pura no existen variables, sólo existen constantes, parámetros y valores, aun cuando en la práctica la mayoría de los lenguajes de programación funcionales no son puros pues retienen algunas nociones de variables y asignaciones. Como no hay variables ni asignación, tampoco existen los ciclos al estilo de los de los lenguajes tradicionales ya que los mismos trabajan con una variable de control que se va reasignando (decrementando o incrementando). Esto se logra en un lenguaje funcional mediante la recursión. Las características más generales de la programación funcional se resumen en: Ausencia de efectos colaterales. El valor de una expresión solo depende de los valores de sus subexpresiones, si las tiene. Se dice que una función (f x y z) tiene un efecto colateral si los valores de x, y, y/o z cambian en el entorno de llamada durante la aplicación de la función a sus argumentos, o si alguna otra acción ocurre mientras se evalúa f. Una función definida con todos los parámetros por valor y donde no se hacen asignaciones a las variables globales, no tiene efectos colaterales. La mayoría de las implementaciones de LISP incorporan algunos efectos colaterales y tipos de datos integrados. Éstos han sido incluidos para hacer más sencillo un código fácilmente legible y las implementaciones eficientes. Semántica limpia Algunas de las características que hacen que un lenguaje sea útil y confiable son en las cuales el lenguaje significa lo que dice -no es ambiguo- y los resultados de
un programa pueden verificarse. En un lenguaje funcional f (3) siempre devolverá el mismo resultado, mientras que en un lenguaje imperativo, como Pascal, éste puede no ser el caso. El manejo del almacenamiento de los datos es implícito, ciertas operaciones asignan almacenamiento en el momento necesario y cuando se vuelve inaccesible o no referenciado se libera automáticamente, con uso de recolección de basuras, código de programa más simple y corto. Las funciones son valores de primera clase. Eso significa que las funciones tienen la misma jerarquía que cualquier otro valor. Una función puede ser el valor de una expresión, puede pasarse como argumento y puede colocarse en una estructura de datos. El estilo de la programación funcional puede ser usado y lo está siendo cada vez más en lenguajes imperativos (Ejemplo: PASCAL) por las mismas razones por las cuales el uso de los lenguajes funcionales se ha incrementado: la simplicidad de la semántica y la resultante claridad de los programas. El requerimiento básico para programar funcionalmente en cualquier lenguaje es que el mismo permita la recursión así como un mecanismo de funciones general y adecuado. Un problema típico en este estilo de programación es el costo de ejecución de los ciclos mediante la recursión. Incluso con los procesadores modernos, que reducen sustancialmente la sobrecarga de la llamada a procedimiento, las implementaciones recursivas son más lentas que las que usan los ciclos standard. PROGRAMACION ORIENTADA A ASPECTOS. Programación Orientada a Aspectos (POA) es un paradigma cuya intención es permitir una adecuada modularización de las aplicaciones, posibilitando mejor separación de incumbencias. Separa componentes de aspectos creando un mecanismo para abstraerlos y componerlos para formar todo el sistema. Un componente es aquella propiedad que se puede encapsular claramente en un procedimiento (un objeto, un método, un API), mientras que un aspecto no se puede encapsular en un procedimiento con los lenguajes tradicionales. ASPECTO: Cada funcionalidad común se encapsulará en una entidad (Aspecto). Unidad modular del programa que aparece en otras unidades funcionales del programa (G. Kiczales). Una incumbencia transversal modularizada.
Los aspectos son propiedades que afectan al rendimiento o la semántica de los componentes. Ejemplos: los patrones de acceso a memoria, la sincronización de procesos concurrentes, el manejo de errores, etc. Fundamentos de la POA Un lenguaje para definir la funcionalidad básica, conocido como lenguaje base. El mismo puede ser un lenguaje imperativo, o no. Por ejemplo C++, Java, PHP, Python, Perl, etc. Uno o varios lenguajes de aspectos, para especificar el comportamiento de los distintos aspectos. Algunos ejemplos son AspectJ, AspectC, phpAspect, Aspyct, Aspect, Cool, RIDL, etc. Un tejedor de aspectos, del inglés weaver, que se encarga de combinar los lenguajes (componentes y aspectos) en tiempo de ejecución o de compilación. Ventajas: Código menos enmarañado, más natural y más reducido. Mayor facilidad para razonar sobre los conceptos, ya que están separados y tienen una dependencia mínima. Facilidad en depurar y modificar el código. Modificaciones grandes en la definición de una materia tenga un impacto mínimo en las otras. Código más reusable y que se puede acoplar y desacoplar cuando sea necesario. Desventajas: El diseño de los puntos de enlace entre los aspectos es más complejo. Se debe tener en cuenta el análisis de nuevas características dentro del desarrollo de sistemas. Surgen nuevos problemas de choque entre el lenguaje base y el lenguaje funcional. Pueden existir problemas de herencia de un aspecto a otro. QUE SON LOS ARBOLES SINTACTICOS. Tradicionalmente, se conoce como “árbol” (en informática) a una estructura de datos ampliamente usada, que consta de un conjunto de nodos conectados, la cual, lleva su nombra porque su forma imita la de la copa de un árbol, solo que en posición invertida.
Sin embargo, un Árbol Sintáctico tiene una idea que, aunque conserve la misma esencia, va por un camino diferente. Para comprenderla primero hay que aclarar los siguientes términos. Semántica: Estudio del significado de los signos lingüísticos y de sus combinaciones. Es decir, para este tema el significado o “traducción” de los signos válidos para el código, expresiones, operadores, etc. Léxico: Conjunto de palabras de una lengua, de una región, una actividad, etc. Es decir, las “palabras” conocidas o disponibles que corresponden para el contexto del programa o del código, según el lenguaje con el que se esté trabajando y el entorno de desarrollo utilizado. El léxico engloba las palabras, su validez, su función, etc. Sintaxis: Se refiere al ‘ordenamiento correcto de las oraciones’. En informática, es la forma correcta en que deben estar dispuestos los símbolos que componen una instrucción, estos se rigen por un reducido conjunto de reglas gramaticales. Este conjunto de reglas se denomina la sintaxis del lenguaje de programación. Por esta razón, también se habla de reglas sintácticas como sinónimo de reglas gramaticales. Es una clase especial de árbol, se le denomina Árbol Sintáctico es una forma condensada de un árbol de análisis sintáctico, útil para representar instrucciones de lenguajes. Un árbol sintáctico permite demostrar que una secuencia de caracteres es una determinada categoría sintáctica. Llevan su nombre en base a la función que cumple, donde cada nodo representa una operación y los hijos de un nodo son los argumentos de la operación. Un árbol sintáctico puede cumplir tres funciones diferentes: Comprobar el orden en que llegan los tokens (componente léxico es una cadena de caracteres) Construir una representación del programa fuente. Si es sintácticamente correcto generar el error. Un Árbol Sintáctico bien realizado se caracteriza por ser una representación abstracta que va desde las sub-categorías hasta la categoría general, por constar de operadores en nodos no terminales y operandos en nodos terminales. MATRIZ DE TRANSICION DE ESTADOS. Esta es una matriz cuadrada, donde el número de renglones y de columnas será igual al de estados que tenga la cadena de Markov, siendo cada elemento de la matriz, la probabilidad de transición respectiva de pasar del estado que encabeza
el renglón donde está ubicado el elemento hacia el estado encabezado por la columna. Miremos el siguiente ejemplo:
Se observa que para el primer renglón las probabilidades de transición indican que habrá un 50% de los clientes fieles a la marca A, por su parte un 30% cambiara de A B y un 20% cambiara de A C. Obsérvese que la suma se probabilidades de cada renglón es igual a 1. Una manera de visualizar mejor la tabla dada anteriormente es a través de un diagrama de estado. Aquí los estados se indican a través de círculos y las flechas que salen de ellos son las probabilidades de que sus clientes cambien a otro estado. Es decir, las flechas que regresan al mismo estado del que salen, señalan las probabilidades de que los clientes sean retenidos por esa marca en particular.
CALCULO DE PROBABILIDADES DE LA MATRIZ DE TRANSICIÓN Para hallar las probabilidades de los estados dentro de la matriz de transición en un periodo determinado procedemos de la siguiente manera como se muestra en el siguiente ejemplo: Las granjas de cierta región se pueden clasificar con 3 tipos: agrícolas, pecuarias o mixtas. Actualmente 30% son agrícolas, 40% pecuarias y 30% son mixtas. La matriz de transición de un año al siguiente es:
De
acuerdo
a
la
información
dada,
el
Po
es:
Para hallar el valor de las probabilidades en el año siguiente hacemos uso de la multiplicación de las matrices, como sigue Para el estado A:
Por lo que el vector para el año siguiente es:
De igual manera calculamos el porcentaje para cada tipo de granja para el periodo 2, 3, 4 y 5
Obsérvese que el valor de la probabilidad de un estado n está dado por la siguiente expresión:
MATRIZ DE TRANSICIÓN EN ESTADO ESTABLE Un estado es estable cuando ya no hay cambios en el sistema, es decir que se alcanza el equilibrio. Una manera posible de obtener las condiciones del sistema
para el estado estable es repetir iterativamente los cálculos para cada periodo con el fin de hallar el periodo con aquellas probabilidades que se mantienen constantes o no cambian. Sin embargo también es posible utilizar los métodos para resolver sistemas de ecuaciones que nos permiten encontrar directamente estas probabilidades de estado estables. Dado el ejemplo anterior acerca de los tipos de granjas, calcularemos los estados estables a través de los métodos para sistemas de ecuaciones. En primera medida lo que hacemos es hallar la traspuesta de la matriz de transición, es decir:
El sistema de ecuaciones quedaría así:
Esta última ecuación es agregada siguiendo la propiedad de que la sumatoria las probabilidades de los estados debe ser igual a 1.
Utilizando el m茅todo de eliminaci贸n, restamos las ecuaciones (1) y (2) eliminando z.
Ahora sumamos las ecuaciones (3) y (4), multiplicando la ecuaci贸n 4 por 0.2 con el fin de eliminar z
Despejando de la ecuaci贸n (6) y reemplazando en (5), tenemos:
Reemplazando x en (6)
PRÁCTICAS DE CLASE MODO CONSOLA EN PYTHON. Variables, expresiones y sentencias. Valores y tipos. Los valores pertenecen a diferentes tipos: 2 es un entero, y "Hola, Mundo!" es una cadena, llamada así porque contiene una “cadena” de letras. Usted (y el intérprete) pueden identificar cadenas porque están encerradas entre comillas. La sentencia de impresión también trabaja con enteros. >>> print 4 4 Si no está seguro del tipo que un valor tiene, el intérprete le puede decir. >>> type("Hola, Mundo!") <type ’string’> >>> type(17) <type ’int’> >>> type(3.2) <type ’float’> ¿Qué ocurre con valores como "17" y "3.2"? Parecen números, pero están encerrados entre comillas como las cadenas. >>> type("17") <type ’string’>
Ellos son cadenas.
>>> type("3.2") <type ’string’> Variables. Una de las características más poderosas en un lenguaje de programación es la capacidad de manipular variables. Una variable es un nombre que se refiere a un valor. La sentencia de asignación crea nuevas variables y les da valores:
>>> mensaje = "¿Qué Onda?" >>> n = 17 >>> pi = 3.14159 La sentencia print también funciona con variables. >>> print mensaje Qué Onda? >>> print n 17 >>> print pi 3.14159 En cada caso el resultado es el valor de la variable. Las variables también tienen tipos; nuevamente, le podemos preguntar al intérprete cuales son. >>> type(mensaje) <type ’string’> >>> type(n) <type ’int’> >>> type(pi) <type ’float’> El tipo de una variable es el mismo del valor al que se refiere. Nombres de variables y palabras reservadas. Los programadores, generalmente, escogen nombres significativos para sus variables —que especifiquen para qué se usa la variable. Estos nombres pueden ser arbitrariamente largos. Pueden contener letras y números, pero tienen que empezar con una letra. Aunque es legal usar letras mayúsculas, por convención no lo hacemos. Si usted lo hace, recuerde que la capitalización importa, Pedro y pedro son variables diferentes.
El carácter subrayado ( _) puede aparecer en un nombre. A menudo se usa en nombres con múltiples palabras, tales como mi_nombre o precio_del_café_en_china. Si usted le da un nombre ilegal a una variable obtendrá un error sintáctico: >>> 76trombones = "gran desfile" SyntaxError: invalid syntax 76trombones es ilegal porque no empieza con una letra. >>> mas$ = 1000000 SyntaxError: invalid syntax
mas$ es ilegal porque contiene un carácter ilegal, el símbolo $. >>> class = "introducción a la programación" SyntaxError: invalid syntax ¿Qué sucede con class? Resulta que class es una de las palabras reservadas (keywords) de Python. Las palabras reservadas definen las reglas del lenguaje y su estructura, y no pueden ser usadas como nombres de variables. Python tiene veintiocho palabras reservadas: and continue else for import not assert def except from in or break del exec global is pass class elif finally if lambda print raise return try while Evaluando expresiones Una expresión es una combinación de valores, variables y operadores. Si usted digita una expresión en la línea de comandos, el intérprete la avalúa y despliega su resultado:
>>> 1 + 1 2 Un valor, por sí mismo, se considera como una expresión, lo mismo ocurre para las variables. >>> 17 17 Aunque es un poco confuso, evaluar una expresión no es lo mismo que imprimir o desplegar un valor. >>> mensaje = "Como le va, Doc?" >>> mensaje "Como le va, Doc?" >>> print mensaje Como le va, Doc? Cuando Python muestra el valor de una expresión que ha evaluado, utiliza el mismo formato que se usaría para entrar un valor. En el caso de las cadenas, esto Implica que se incluyen las comillas. Cuando se usa la sentencia print, el efecto es distinto como usted ya lo ha evidenciado. Operadores y operandos. Los operadores son símbolos especiales que representan cómputos, como la suma y la multiplicación. Los valores que el operador usa se denominan operandos. Los siguientes son expresiones validas en Python, cuyo significado es más o menos claro: 20+32 hora-1 hora*60+minuto minuto/60 5**2 (5+9)*(15-7) Los símbolos +, -, y /, y los paréntesis para agrupar, significan en Python lo mismo que en la matemática. El asterisco (*) es el símbolo para la multiplicación, y ** es el símbolo para la exponenciación.
Cuando el nombre de una variable aparece en lugar de un operando, se reemplaza por su valor antes de calcular la operación La suma, resta, multiplicación y exponenciación realizan lo que usted esperaría, pero la división podría sorprenderlo. La siguiente operación tiene un resultado inesperado: >>> minuto = 59 >>> minuto/60 0 El valor de minuto es 59, y 59 dividido por 60 es 0.98333, no 0. La razón para esta discrepancia radica en que Python est{a realizando división entera. Cuando los dos operandos son enteros el resultado también debe ser un entero; y, por convención, la división entera siempre redondea hacia abajo, incluso en casos donde el siguiente entero está muy cerca. Orden de las operaciones Cuando hay depende de precedencia matemáticos. operaciones:
más de un operador en una expresión, el orden de evaluación las reglas de precedencia. Python sigue las mismas reglas de a las que estamos acostumbrados para sus operadores El acrónimo PEMDAS es útil para recordar el orden de las
Los Paréntesis tienen la precedencia más alta y pueden usarse para forzar la evaluación de una expresión de la manera que usted desee. Ya que las expresiones en paréntesis se evalúan primero: 2 * (3-1) es 4, y (1+1)**(5-2) es 8. Usted también puede usar paréntesis para que una expresión quede más legible, como en (minuto * 100) / 60, aunque esto no cambie el resultado. La Exponenciación tiene la siguiente precedencia más alta, así que 2**1+1 es 3 y no 4, y 3*1**3 es 3 y no 27. La Multiplicación y la División tienen la misma precedencia, aunque es más alta que la de la Adición y la Sustracción, que también tienen la misma precedencia.
Así que 2*3-1 es 5 en lugar de 4, y 2/3-1 es -1, no 1 (recuerde que en división entera, 2/3=0). Los operadores con la misma precedencia se evalúan de izquierda a derecha. Minuto=59, en la expresión minuto*100/60; la multiplicación se hace primero, resultando 5900/60, lo que a su vez da 98. Si las operaciones se hubieran evaluado de derecha a izquierda, el resultado sería 59/1, que es 59, y no es lo correcto. Operaciones sobre cadenas En general, usted no puede calcular operaciones matemáticas sobre cadenas, incluso si las cadenas lucen como números. Las siguientes operaciones son ilegales (asumiendo que mensaje tiene el tipo cadena): mensaje-1 "Hola"/123 mensaje*"Hola" "15"+2 Sin embargo, el operador + funciona con cadenas, aunque no calcula lo que usted esperaría. Para las cadenas, el operador + representa la concatenación, que significa unir los dos operandos enlazándolos en el orden en que aparecen. Por ejemplo: fruta = "banano" bienCocinada = " pan con nueces" >>>print fruta + bienCocinada La salida de este programa es: banano pan con nueces. El espacio antes de la palabra pan es parte de la cadena y sirve para producir el espacio entre las cadenas concatenadas. El operador * también funciona con las cadenas; hace una repetición. Por ejemplo, ’Fun’*3 es ’FunFunFun’. Uno de los operados tiene que ser una cadena, el otro tiene que ser un entero. Estas interpretaciones de + y * tienen sentido por la analogía con la suma y la multiplicación. Así como 4*3 es equivalente a 4+4+4, esperamos que "Fun"*3 sea lo mismo que "Fun"+"Fun"+"Fun", y lo es. Practica 2 Composición
Hasta aquí hemos considerado a los elementos de un programa—variables, expresiones y sentencias—aisladamente, sin especificar como combinarlos. Una de las características más útiles de los lenguajes de programación es su capacidad de tomar pequeños bloques para componer con ellos. Por ejemplo, ya que sabemos cómo sumar números y cómo imprimirlos; podemos hacer las dos cosas al mismo tiempo: >>> print 17 + 3 20 De hecho, la suma tiene que calcularse antes que la impresión, así que las acciones no están ocurriendo realmente al mismo tiempo. El punto es que cualquier expresión que tenga números, cadenas y variables puede ser usada en una sentencia de impresión (print). Usted ha visto un ejemplo de esto: >>>minuto=60 >>>hora=60 >>>print "Número de minutos desde media noche: ", hora*60+minuto Comentarios A medida que los programas se hacen más grandes y complejos, se vuelven más difíciles de leer. Los lenguajes formales son densos; y, a menudo, es difícil mirar una sección de código y saber qué hace, o por qué lo hace. Por esta razón, es una muy buena idea a ˜nadir notas a sus programas para explicar, en lenguaje natural, lo que hacen. Estas notas se denominan comentarios y se marcan con el símbolo #: >>># calcula el porcentaje de la hora que ha pasado >>>porcentaje = (minuto * 100) / 60 En este caso, el comentario aparece en una línea completa. También pueden ir comentarios al final de una línea: >>># precaución: división entera porcentaje = (minute * 100) / 60
Glosario Valor: un número o una cadena (u otra cosa que se introduzca más adelante) que puede ser almacenado en una variable o calculado en una expresión. Tipo: conjunto de valores. El tipo del valor determina como se puede usar en expresiones. Hasta aquí, los tipos que usted ha visto son enteros (tipo int), números de punto flotante (tipo float) y cadenas (tipo string). Punto flotante: formato para representar números con parte decimal. Variable: nombre que se refiere a un valor. Sentencia: sección de código que representa un comando o acción. Hasta aquí las sentencias que usted ha visto son la de asignación y la de impresión. Asignación: corresponde a la sentencia que pone un valor en una variable. Palabra reservada: es una palabra usada por el compilador para analizar sintácticamente un programa; usted no puede usar palabras reservadas como if, def, y while como nombres de variables. Operador: símbolo especial que representa un simple cálculo como una suma, multiplicación o concatenación de cadenas. Operando: uno de los valores sobre el cual actúa un operador. Expresión: combinación de variables, operadores y valores que representa un único valor de resultado. Evaluar: simplificar una expresión ejecutando varias operaciones a fin de retornar un valor único. División entera: operación que divide un entero por otro y retorna un entero. La división entera retorna el número de veces que el denominador cabe en el numerador y descarta el residuo. Reglas de precedencia: reglas que gobiernan el orden en que las expresiones que tienen múltiples operadores y operandos se evalúan. Concatenar: unir dos operandos en el orden en que aparecen. Composición: es la capacidad de combinar simples expresiones y sentencias dentro de sentencias y expresiones compuestas para representar cálculos complejos concisamente.
Comentario: información que se incluye en un programa para otro programador (o lector del código fuente) que no tiene efecto en la ejecución. Conversión de tipos Python proporciona una colección de funciones que convierten valores de un tipo a otro. La función int toma cualquier valor y lo convierte a un entero, si es posible, de lo contrario se queja: >>> int("32") 32 >>> int("Hola") ValueError: invalid literal for int(): Hola int también puede convertir valores de punto flotante a enteros, pero hay que tener en cuenta que va a eliminar la parte decimal: >>> int(3.99999) 3 >>> int(-2.3) -2 La función float convierte enteros y cadenas a número de punto flotante: >>> float(32) 32.0 >>> float("3.14159") 3.14159 Finalmente, la función str convierte al tipo cadena (string): >>> str(32) ’32’ >>> str(3.14149) ’3.14149’
Coerción de tipos Ahora que podemos convertir entre tipos, tenemos otra forma de esquivar a la división entera. Retomando el ejemplo del capítulo anterior, suponga que deseamos calcular la fracción de una hora que ha transcurrido. La expresión más obvia minuto/60, hace división entera, así que el resultado siempre es 0, incluso cuando han transcurrido 59 minutos. Una solución es convertir minuto a punto flotante para realizar la división en punto flotante: >>> minuto = 59 >>> float(minute)/60.0 0.983333333333 Otra alternativa es sacar provecho de las reglas de conversión automática de tipos, que se denominan coerción de tipos. Para los operadores matemáticos, si algún operando es un número flotante, el otro se convierte automáticamente a flotante: >>> minuto = 59 >>> minuto / 60.0 0.983333333333 Así que haciendo el denominador flotante, forzamos a Python a realizar división en punto flotante. Practica 3 Operadores aritméticos Operador
Descripción Ejemplo
+
Suma r = 3 + 2 # r es 5
-
Resta r = 4 - 7 # r es -3
Operador
Descripción Ejemplo
-
Negación
r = -7 # r es -7
*
Multiplicación
r = 2 * 6 # r es 12
**
Exponente
r = 2 ** 6 # r es 64
/
División
r = 3.5 / 2 # r es 1.75
//
División entera
%
Módulo
r = 3.5 // 2 # r es 1.0
r = 7 % 2 # r es 1
Booleanos Una variable de tipo boolea¬no sólo puede tener dos valores: True (cierto) y False (falso). Estos son los distintos tipos de operadores con los que podemos trabajar con valores booleanos, los llamados operadores lógicos o condicionales: Operador
Descripción Ejemplo
and
¿Se cumple a y b? r = True and False # r es False
or
¿Se cumple a o b? r = True or False # r es True
not
No a r = not True # r es False
Los valores booleanos son además el resultado de expresiones que utilizan operadores relacionales (comparaciones entre valores): Operador
Descripción Ejemplo
==
¿son iguales a y b?
r = 5 == 3 # r es False
!=
¿son distintos a y b?
r = 5 != 3 # r es True
<
¿es a menor que b?
r = 5 < 3 # r es False
>
¿es a mayor que b?
r = 5 > 3 # r es True
<=
¿es a menor o igual que b?
r = 5 <= 5 # r es True
>=
¿es a mayor o igual que b?
r = 5 >= 3 # r es True
Listas La lista es un tipo de colección ordenada. Sería equivalente a lo que en otros lenguajes se conoce por arrays, o vectores. Las listas pueden contener cualquier tipo de dato: números, cadenas, booleanos, … y también listas.
Crear una lista es tan sencillo como indicar entre corchetes, y separados por comas, los valores que queremos incluir en la lista: >>>a = [22, True, “una lista”, [1, 2]] Podemos acceder a cada uno de los elementos de la lista escribiendo el nombre de la lista e indicando el índice del elemento entre corchetes. NOTA: Ten en cuenta que el índice del primer elemento de la lista es 0, y no 1: >>>a[2] ‘una lista’ >>>a[0] 22 Si queremos acceder a un elemento de una lista incluida dentro de otra lista. >>> a = [22, True, “una lista”, [1, 2]] Tendremos que utilizar dos veces este operador, primero para indicar a qué posición de la lista exterior queremos acceder, y el segundo para seleccionar el elemento de la lista interior: si queremos el valor 1 de la sublista [1,2] >>>a[3][0] 1 También podemos utilizar este operador para modificar un elemento de la lista si lo colocamos en la parte izquierda de una asignación: >>> a = [22, True, “una lista”, [1, 2]] >>>a[0] 22 >>>a[0] =99 >>>a[0] 99
Una curiosidad sobre el operador [] de Python es que podemos utilizar también números negativos. Si se utiliza un número negativo como índice, esto se traduce en que el índice empieza a contar desde el final, hacia la izquierda; es decir, con [1] accederíamos al último elemento de la lista, con [-2] al penúltimo, con [-3], al antepenúltimo, y así sucesivamente. >>>a = [22, True, “una lista”, [1, 2]] >>>a[-1] [1,2] Sentencias condicionales Aquí es donde cobran su importancia el tipo booleano y los operadores lógicos y relacionales que aprendimos en el capítulo sobre los tipos básicos de Python. if La forma más simple de un estamento condicional es un if (del inglés sí) seguido de la condición a evaluar, dos puntos (:) y en la siguiente línea e indentado, el código a ejecutar en caso de que se cumpla dicha condición. >>> valor=”programa” >>> if valor == “programa” : print “Respuesta correcta” if … else >>> valor=”programa” >>> if valor == “programa” : print “Respuesta correcta” …else: print “respuesta incorrecta” Respuesta correcta if … elif … elif … else elif es una contracción de else if, por lo tanto elif numero > 0 puede leerse como “si no, si número es mayor que 0”. Es decir, primero se evalúa la condición del if. Si es cierta, se ejecuta su código y se continúa ejecutando el código posterior al condicional; si no se cumple, se evalúa la condición del elif. Si se cumple la condición del elif se ejecuta su código y se continua ejecutando el código posterior al condicional; si no se cumple y hay más de un elif se continúa con el siguiente en orden de aparición. Si no se cumple la condición del if ni de ninguno de los elif, se ejecuta el código del else.
>>>numero=1 >>>if numero >0: print “número positivo” …elif numero <0: print “número negativo” …else: print “Cero” número positivo Practica 4 Entrada estándar La forma más sencilla de obtener información por parte del usuario es mediante la función raw_input. Esta función toma como parámetro una cadena a usar como prompt (es decir, como texto a mostrar al usuario pidiendo la entrada) y devuelve una cadena con los caracteres introducidos por el usuario hasta que pulsó la tecla Enter. Veamos un pequeño ejemplo: >>>nombre = raw_input(“dame tu nombre: “) Definición de una Función >>> def imprimedoble(dato): …
print dato dato
Esta función toma un argumento y lo asigna a un parámetro llamado dato. El valor del parámetro (en este momento no tenemos idea de lo que será) se imprime dos veces. >>>imprimedoble(“azul”) azul azul >>>imprimedoble(“azul”*3) azulazulazul azulazulazul >>> def concatenarDoble(parte1, parte2): …
cat = parte1 + parte2
…
imprimaDoble(cat)
>>>concatenarDoble(“uno”,”dos”)
unodos unodos def conteo(n): if n == 0: print "Despegue!" else: print n conteo(n-1) RESOLUCIÓN DE EJERCICIOS EN SHARPDEVELOP. Crear y ejecutar en el editor SharpDevelop la siguiente función de Ironpython def conteo(n): if n == 0: print "Despegue!" else: print n conteo(n-1) a=raw_input("presiona enter") conteo(10) Crear y ejecutar en el editor SharpDevelop la siguiente lista de datos, y con la función len() contar el número de letras de cada palabra en la lista de datos. Utilice la instrucción for para revisar cada palabra de la lista. palabras= ["windows","universidad","ingenieria de software","interpretes"] for i in palabras: print (i, len(i)) a=raw_input("presiona enter") Crear y ejecutar en el editor SharpDevelop un programa que solicite la edad y nos indique cuantos días hemos vivido, además que se valide que se capturó un número y no una letra. try: edad =raw_input("Cuál es tu edad: ") días=int(edad)*365 print "Has vivido " + str(días) + " días" a=raw_input("presiona enter") except ValueError:
print "Eso no es un número, verifica!" a=raw_input("presiona enter") Operaciones básicas. num1=raw_input("dame el primer número") num2=raw_input("dame el segundo número") suma=int(num1)+int(num2) print("el resultado de la suma es: ",suma) #a=raw_input("presione enter para continuar") resta=int(num1)-int(num2) print("el resultado de la resta es: ",resta) #a=raw_input("presione enter para continuar") multiplicación=int(num1)*int(num2) print("el resultado de la multiplicación es: ",multiplicación) #a=raw_input("presione enter para continuar") elevar=int(num1)**int(num2) print("el resultado de la elevación es: ",elevar) #a=raw_input("presione enter para continuar") if int(num2)!=0: division=int(num1)/int(num2) print("el resultado de la division es: ",float(division)) a=raw_input("presione enter para continuar") Arreglo de tres palabras y cambiar palabra. a= raw_input ("Dame tres palabras: ") b= raw_input (", " ) c= raw_input(", " ) palabras = [a, b, c] print (palabras) p= raw_input ("desea cambiar una palabra?. Escriba SI o NO: " ) if p == 'SI': k= raw_input("que palabra desea cambiar? " ) if k== a: a= raw_input("Dame la nueva palabra: " ) elif k== b: b= raw_input ("Dame la nueva palabra: " ) elif k== c: c=raw_input ("Dame la nueva palabra: " ) else: print ("La palabra no es correcta") else: print ("No se cambio ninguna palabra") palabras = [a, b, c] print (palabras) elif o == '6':
num1=raw_input("dame el primer numero: ") num2=raw_input("dame el segundo numero: ") elevar=int(num1)**int(num2) print("el resultado de la elevacion es: ",elevar) elif o == '7': def conteo(n): if n == 0: print "Despegue!" else: print n conteo(n-1) a=raw_input("presiona enter") conteo(10) elif o == '8': ñ= raw_input("presione enter para salir") break Menú de operaciones. while True: print("1.suma") print("2.resta") print("3.division") print("4.multiplicacion") print("5.arreglo") print("6.elevar") print("7.Instr. for") print("8.Salir") o=raw_input("Seleccione la operación que desea hacer: " ) while o > '8' or o == '0' or o >= '10': print("¡Ha escrito un número incorrecto! Inténtelo de nuevo") o=raw_input("Seleccione la operación que desea hacer: " ) if o == '1': num1=raw_input("dame el primer numero: ") num2=raw_input("dame el segundo numero: ") suma=int(num1)+int(num2) print("el resultado de la suma es: ",suma) elif o == '2': num1=raw_input("dame el primer numero: ") num2=raw_input("dame el segundo numero: ") resta=int(num1)-int(num2) print("el resultado de la resta es: ",resta) elif o =='3': num1=raw_input("dame el primer numero: ") num2=raw_input("dame el segundo numero: ") if int(num2)!=0: division=int(num1)/int(num2)
print("el resultado de la division es: ",float(division)) else: print("No puede dividir entre 0") elif o == '4': num1=raw_input("dame el primer numero: ") num2=raw_input("dame el segundo numero: ") multiplicacion=int(num1)*int(num2) print("el resultado de la multiplicacion es: ",multiplicacion) elif o == '5': a= raw_input ("Dame tres palabras: ") b= raw_input (", " ) c= raw_input(", " ) palabras = [a, b, c] print (palabras) p= raw_input ("desea cambiar una palabra?. Escriba SI o NO: " ) if p == 'SI': k= raw_input("que palabra desea cambiar? " ) if k== a: a= raw_input("Dame la nueva palabra: " ) elif k== b: b= raw_input ("Dame la nueva palabra: " ) elif k== c: c=raw_input ("Dame la nueva palabra: " ) else: print ("La palabra no es correcta") else: print ("No se cambio ninguna palabra") palabras = [a, b, c] print (palabras) elif o == '6': num1=raw_input("dame el primer numero: ") num2=raw_input("dame el segundo numero: ") elevar=int(num1)**int(num2) print("el resultado de la elevacion es: ",elevar) elif o == '7': def conteo(n): if n == 0: print "Despegue!" else: print n conteo(n-1) a=raw_input("presiona enter") conteo(10) elif o == '8': 単= raw_input("presione enter para salir") break
Tablas de multiplicar. numero = raw_input("que tabla quieres?: ") print "tabla del",int(numero) for v in range(1,11): print numero,"x",v,"=",int(numero) * variable a=raw_input("precione enter para salir") Tablas de multiplicar (da la tabla del 1 hasta la tabla que pidas). while True: numero = raw_input("que tabla quieres?: ") print "tabla del",int(numero) for v in range(1,int(numero) + 1): for j in range(1,11): print v,"x",j,"=",v * j a=raw_input("Desea otra operacion (S/N): ") if a == 'N': break
CONCLUSION. En conclusión se cumplió con los objetivos trazados en la materia de Intérpretes y llegamos a comprender que un intérprete es un programa que lee línea a línea un programa escrito en un lenguaje; en lenguaje fuente y lo va traduciendo a un código intermedio, para ejecutarlo. Por mi parte no me queda más que agradecer la forma en que fue impartida la materia ya que los interpretes son de suma importancia para nuestra carrera ya que nos puede servir como una herramienta para comprender la forma de programar.