FUNDAMENTOS DE PROGRAMACIÓN I. CONCEPTOS BÁSICOS SOBRE PROGRAMACIÓN 1.1 DEFINCIONES FUNDAMENTALES 1.1.1 Software El software se define como el grupo de instrucciones que debe realizar el sistema para realizar una tarea específica. Es un intangible de los sistemas pero parte fundamental de ellos. El software se divide en dos grandes grupos: software del sistema y software de aplicaciones. Software del Sistema: Es el conjunto de programas indispensables para que la máquina funcione; se denominan también programas del sistema. Estos programas son, básicamente, el sistema operativo, los editores de texto, los compiladores/intérpretes (lenguajes de programación) y los programas de utilidad. Los programas de utilidad facilitan el uso de la computadora. Un buen ejemplo es un editor de textos que permite la escritura y edición de documentos. Software de Aplicación: Los programas que realizan tareas concretas, nóminas, contabilidad, control de inventarios, etc. Es decir, los programas que podrá escribir en C, se denominan programas de aplicación. A lo largo del curso se realizaran pequeños programas de aplicación que muestran los principios de una buena programación de computadora.
1.1.2 Programación La programación se define como el proceso en el cual se convierten especificaciones generales de un sistema en instrucciones utilizables por el ordenador para que este produzca los resultados deseados, también se conoce como desarrollo de software. En términos más generales la programación permite convertir en información útil los datos obtenidos por medio de un proceso específico.
1.1.3 Instrucciones Se definen como los diferentes pasos o acciones que están expresadas en los algoritmos que forman los programas. Se denominan también sentencias o proposiciones (normalmente el término instrucción se suele referir a los lenguajes máquina y bajo nivel, reservando la sentencia o proposición para los lenguajes de alto nivel) Por consiguiente, un programa consta de una secuencia de instrucciones, cada una de las cuales especifica ciertas operaciones que debe ejecutar la computadora. Las instrucciones básicas y comunes a casi todos los lenguajes de programación se pueden condensar en cuatro grupos: • Instrucciones de entrada / salida: Instrucciones de transferencia de información y datos entre dispositivos periféricos y la memoria central. • Instrucciones aritmético – lógicas: Instrucciones que ejecutan operaciones aritméticas (suma, resta, multiplicación, división)
• Instrucciones selectivas: Permite la selección de tareas alternativas en función de los resultados de diferentes expresiones condicionales • Instrucciones repetitivas: permiten la repetición de secuencias instrucciones un número determinado de veces.
1.1.4 Programa De esta manera, el proceso de tratamiento de los datos que se lleva a cabo, se implementa por medio de una lista de instrucciones que la computadora debe seguir y a lo cual denominaremos programa. Las instrucciones se componen de enunciados usados en lenguajes de programación como Basic, C ó java. Un programa de cómputo debe: • • • • •
ser confiable y funcional advertir errores de entrada obvios y comunes (validar datos de entrada) Estar documentado adecuadamente Ser comprensible Estar codificado en el lenguaje apropiado
1.1.5 Procesamiento de Datos Consiste en la recolección de datos de entrada que son evaluados y ordenados para ser colocados de manera que produzcan información útil. Los datos son las características propias de cualquier entidad. Por ejemplo: los datos de una persona como su edad, fecha de nacimiento, domicilio, número de teléfono, etc. La información la definimos como el conocimiento relevante producido como resultado del procesamiento de datos y adquirido por la gente para realzar el entendimiento y cumplir ciertos propósitos. Dentro de la actividad concerniente al procesamiento de los datos, están: • Captura de datos de entrada. • Manejo de los datos (incluye clasificación, ordenación, cálculo y sumarización de éstos). • Administración de la salida resultante (gestión de la información).
1.2 LENGUAJE DE PROGRAMACIÓN Definiremos los lenguajes de programación como un número determinado de símbolos que junto a un conjunto de reglas representan un programa. Estos lenguajes de programación, igual que a los lenguajes de comunicación, constan de un léxico, una sintaxis y una semántica. Léxico: Se define como el conjunto de símbolos o palabras que se usan. Sintaxis: Estudia las formas en que se combinan las palabras, así como las relaciones existentes entre ellas. Semántica: Permite determinar el significado de cualquier construcción del lenguaje.
Los lenguajes los podemos clasificar en lenguajes de máquina, de bajo nivel y de alto nivel, atendiendo esta clasificación al número de instrucciones necesarias que se deben ejecutar para realizar una tarea específica.
1.2.1 Lenguajes máquina (Primera generación) Los lenguajes máquina son aquellos que están escritos en lenguajes directamente inteligibles por la computadora, ya que sus instrucciones son cadenas binarias que especifican una operación o una dirección de memoria. El código máquina es el conocido código binario. Las instrucciones en lenguaje máquina dependen del hardware de la computadora y, por tanto, diferirán de una computadora a otra. El lenguaje máquina de un PC (computadora personal) será diferente de un sistema HP (Hewlett Packard), Compaq o un sistema de IBM. Las ventajas de programar en lenguaje máquina son la posibilidad de cargar (transferir un programa a la memoria) sin necesidad de traducción posterior, lo que supone una velocidad de ejecución superior a cualquier otro lenguaje de programación. Los inconvenientes -en la actualidad- superan a las ventajas, lo que hace prácticamente no recomendables los lenguajes máquina. Estos inconvenientes son: • • • •
Dificultad y lentitud en la codificación. Poca fiabilidad. Dificultad grande de verificar y poner a punto los programas. Los programas sólo son ejecutables en el mismo procesador.
1.2.2 Lenguajes de Bajo Nivel (Segunda generación) Los lenguajes de bajo nivel son más fáciles de utilizar que los lenguajes máquina, pero, al igual, que ellos, dependen de la máquina en particular. El lenguaje de bajo nivel por excelencia es el lenguaje ensamblador (assembler), usado en la programación de PIC’s o microcontroladores antiguos. Las instrucciones en lenguaje ensamblador son instrucciones conocidas como nemotécnicos. Por ejemplo, nemotécnicos típicos de operaciones aritméticas son: en inglés, ADD, SUB, DIV, etc.; en español, SUM, RES, DIV, etc. Una instrucción típica de suma sería: ADD M, N, P Esta instrucción podía significar «sumar el número contenido en la posición de memoria M a1 número almacenado en la posición de memoria N y situar el resultado en la posición de memoria P ». Evidentemente, es mucho más sencillo recordar la instrucción anterior con un nemotécnico que su equivalente en código máquina: 0110 1001 1010 1011 Un programa escrito en lenguaje ensamblador no puede ser ejecutado directamente por la computadora -en esto se diferencia esencialmente del lenguaje máquina-, sino que requiere una fase de traducción al lenguaje máquina.
El programa original escrito en lenguaje ensamblador se denomina programa fuente y el programa traducido en lenguaje máquina se conoce como programa objeto, ya directamente inteligible por la computadora.
Programa Fuente en ensamblador
Programa ENSAMBLADOR (Assembler)
Programa Objeto en Código Máquina
Los inconvenientes más notables de los lenguajes ensambladores son: • Dependencia total de la máquina, lo que impide la transportabilidad de los programas (posibilidad de ejecutar un programa en diferentes máquinas). El lenguaje ensamblador del PC es distinto del lenguaje ensamblador del Apple Macintosh. • La formación de los programas es más compleja que la correspondiente a los programadores de alto nivel, ya que exige no sólo las técnicas de programación, sino también el conocimiento del interior de la máquina.
1.2.3 Lenguaje de alto nivel (Tercera generación) Los lenguajes de alto nivel son los más utilizados por los programadores. Están diseñados para que las personas escriban y entiendan los programas de un modo mucho más fácil que los lenguajes máquina y ensambladores. Otra razón es que un programa escrito en lenguaje de alto nivel es independiente de la máquina; esto es, las instrucciones del programa de la computadora no dependen del diseño del hardware o de una computadora en particular. En consecuencia, los programas escritos en lenguaje de alto nivel son portables o transportables, lo que significa la posibilidad de poder ser ejecutados con poca o ninguna modificación en diferentes tipos de computadoras; al contrario que los programas en lenguaje máquina o ensamblador, que sólo se pueden ejecutar en un determinado tipo de computadora. Los lenguajes de alto nivel presentan las siguientes ventajas: • El tiempo de formación de los programadores es relativamente corto comparado con otros lenguajes. • La escritura de programas se basa en reglas sintácticas similares a los lenguajes humanos. Nombre de las instrucciones tales como READ, WRITE, COPY. • Las modificaciones y puestas a punto de los programas son más fáciles. • Reducción del coste de los programas. • Transportabilidad. Los inconvenientes se concretan en: • Incremento del tiempo de puesta a punto, al necesitarse diferentes traducciones del programa • No se aprovechan los recursos internos de la máquina, que se explotan mucho mejor en lenguajes de máquina y ensambladores • Aumento de la ocupación de memoria. • El tiempo de ejecución de los programas es mucho mayor. • AI igual que sucede con los lenguajes ensambladores, los programas fuente tienen que ser traducidos para que sean entendidos por la máquina, estos traductores se denominan interpretes o compiladores. En un lenguaje de bajo nivel cada instrucción corresponde a una acción ejecutable
por el ordenador, mientras que en los lenguajes de alto nivel una instrucción suele corresponder a varias acciones. Los lenguajes de alto nivel son independientes de la arquitectura física de la computadora. Permiten usar los mismos programas en computadoras de diferentes arquitecturas (portabilidad), y no es necesario conocer el hardware específico de la máquina. La ejecución de un programa en lenguaje de alto nivel, requiere de una traducción del mismo al lenguaje de la computadora donde va a ser ejecutado. Una sentencia en un lenguaje de alto nivel da lugar, al ser traducida, a varias instrucciones en lenguaje entendible por el computador. Utilizan notaciones cercanas a las usadas por las personas en un determinado ámbito. Se suelen incluir instrucciones potentes de uso frecuente que son ofrecidas por el lenguaje de programación.
1.2.4 Lenguajes de última generación Dentro de esta clasificación encontramos los lenguajes orientados a problemas (4 GL) y los lenguajes naturales. Los lenguajes orientados a problemas resultan mas eficaces para la resolución de un tio de problemas a consta de una menor eficiencia para otros. Requieren poca capacitación especial por parte del usuario, son considerados de muy alto nivel y estan diseñados para resolver problemas específicos. Incluye un lenguaje de consulta y un generador de aplicaciones. • Lenguajes de consulta: Permiten a no programadores usar ciertos comandos de fácil comprensión para la búsqueda y generación de reportes a partir de una base de datos. • Generador de aplicaciones: Quiere decir que cuando se diseña uno de estos lenguajes, se tiene en cuenta que su finalidad es la resolución de problemas, prescindiendo de la arquitectura del computador. Contiene varios módulos que han sido preprogramados para cumplir varias tareas. Por su parte, los lenguajes naturales son lenguajes orientados a aplicaciones en inteligencia artificial, como lisp y prolog. Dentro de este campo destacan las aplicaciones en sistemas expertos, juegos, visión artificial (Jurasic Park) y robótica. Lisp es un lenguaje para procesamiento de listas y manipulación de símbolos. Prolog es un lenguaje basado en la lógica, para aplicaciones de bases de datos e Inteligencia Artificial.
1.3 TRADUCTORES DEL LENGUAJE Los traductores de lenguaje son programas que traducen, valga la redundancia, los programas escritos en lenguajes de alto nivel a lenguaje de máquina, es decir a valores binarios que el ordenador pueda interpretar. Los traductores tienen dos clasificaciones, los intérpretes y los compiladores. 1.3.1 Intérpretes Un intérprete es un traductor que toma un programa fuente, lo traduce y a continuación lo ejecuta. Existen programas intérpretes clásicos como BASIC. Prácticamente ya no se utilizan, aunque las versiones Qbasic y QuickBASIC todavía se pueden encontrar y
corren en las computadoras personales. Sin embargo, está muy extendida la versión interpretada del lenguaje Smalltalk, un lenguaje orientado a objetos puro. 1.3.2 Compiladores Un compilador es un programa que traduce los programas fuente escritos en lenguaje de alto nivel, como lo son C y JAVA, a lenguaje de máquina. Los programas escritos en lenguaje de alto nivel se llaman programas.fuente y el programa traducido programa objeto o código objeto. El compilador traduce sentencia a sentencia- el programa fuente. Los lenguajes compiladores típicos son: C, C++, Pascal, Java y COBOL. A continuación se presenta un diagrama en donde se detalla la forma de actuar de un intérprete y un compilador.
Programa Fuente
Programa Fuente
Intérprete
Compilador
Traducción y Ejecución Línea a Línea
Programa Objeto
Intérprete
Compilador
1.3.3 Fases de la compilación Como habíamos mencionado, la compilación es el proceso de traducción de programas fuente a programas objeto. El programa objeto obtenido de la compilación ha sido traducido normalmente a código máquina. Para conseguir el programa máquina real se debe utilizar un programa llamado montador o enlazador (linker). El proceso de montaje conduce a un programa en lenguaje máquina directamente ejecutable, tal como se muestra en la figura siguiente.
Programa Fuente
Compilador
Programa Objeto
Montador
Programa ejecutable en lenguaje máquina
El proceso de ejecución de un programa escrito en un lenguaje de programación y mediante un compilador suele tener los siguientes pasos: Escritura del programa fuente con un editor (programa que permite a una computadora actuar de modo similar a una máquina de escribir electrónica) y guardarlo en un dispositivo de almacenamiento (por ejemplo, un disco).
1. 2. 3. 4. 5. 6.
Introducir el programa fuente en memoria. Compilar el programa con el compilador C. Verificar y corregir errores de compilación (listado de errores). Obtención del programa objeto El enlazador (linker) obtiene el programa ejecutable. Se ejecuta el programa y, si no existen errores, se tendrá la salida del programa.
A continuación se detalla el proceso completo y específico de ejecución de programas en lenguaje C.
Programa Fuente
Compilador Si Errores de Compilación
Programa Objeto
Montador
Prog Ejecutable
Ejecución
1.4 ALGORITMO El programador de computadora es antes que nada una persona que resuelve problemas, por lo que para llegar a ser un programador eficaz se necesita aprender a resolver problemas de un modo riguroso y sistemático. A lo largo del curso nos referiremos a la metodología necesaria para resolver problemas mediante programas, concepto que se denomina metodología de la programación. El eje central de esta metodología es el concepto, ya tratado, de algoritmo. Un algoritmo es un método para resolver un problema. Se entiende algoritmo entonces como un conjunto de instrucciones, donde se especifica detalladamente cada una de las instrucciones, en forma sucesiva, para resolver un problema determinado. Aunque la popularización del término ha llegado con el advenimiento de la era informática, algoritmo proviene de Mohammed al-KhoWirizmi, matemático persa
que vivió durante el siglo IX y alcanzó gran reputación por el enunciado de las reglas paso a paso para sumar, restar, multiplicar y dividir números decimales; la traducción al latín del apellido en la palabra algorismus derivó posteriormente en algoritmo. Euclides, el gran matemático griego (del siglo IV a.c.) que inventó un método para encontrar el máximo común divisor de dos números, se considera con Al-Khowirizmi el otro gran padre de la algoritmia. La resolución de un problema exige el diseño de un algoritmo que resuelva el problema propuesto.
Problema
Diseño del algoritmo
Programa de Computadora
Los pasos para la resolución de un problema, a nivel general, son: 1. Diseño del algoritmo que describe la secuencia ordenada de pasos -sin ambigüedades- que conducen a la solución de un problema dado. (Análisis del problema y desarrollo del algoritmo.) 2. Expresar el algoritmo como un programa en un lenguaje de programación adecuado. (Fase de codificación.) 3. Ejecución y validación del programa por la computadora. Para llegar a la realización de un programa es necesario el diseño previo de un algoritmo, de modo que sin algoritmo no puede existir un programa. Los algoritmos son independientes tanto del lenguaje de programación en que se expresan como de la computadora que los ejecuta. En cada problema el algoritmo se puede expresar en un lenguaje diferente de programación y ejecutarse en una computadora distinta; sin embargo, el algoritmo será siempre el mismo. Así, por ejemplo, en una analogía con la vida diaria, una receta de un plato de cocina se puede expresar en español, inglés o francés, pero cualquiera que sea el lenguaje, los pasos para la elaboración del plato se realizarán sin importar el idioma del cocinero. En la ciencia de la computación y en la programación, los algoritmos son más importantes que los lenguajes de programación o las computadoras. Un lenguaje de programación es tan sólo un medio para expresar un algoritmo y una computadora es sólo un procesador para ejecutarlo. Tanto el lenguaje de programación como la computadora son los medios para obtener un fin: conseguir que el algoritmo se ejecute y se efectúe el proceso correspondiente. El diseño de la mayoría de los algoritmos requiere creatividad y conocimientos profundos de la técnica de la programación. En esencia, la solución de un problema se puede expresar mediante un algoritmo. 1.4.1 Características del algoritmo Las características fundamentales que debe cumplir todo algoritmo son: • • •
Un algoritmo debe ser preciso e indicar el orden de realización de cada paso. Un algoritmo debe estar definido. Si se sigue un algoritmo dos veces, se debe obtener el mismo Un algoritmo debe ser finito. Si se sigue un algoritmo, se debe terminar en algún momento; o sea, debe tener un número finito de pasos.
1.4.2 Diseño del algoritmo En la etapa de análisis del proceso de programación se determina qué hace el programa. En la etapa de diseño se determina como hace el programa la tarea solicitada. Los métodos más eficaces para el proceso de diseño se basan en el conocido por divide y vencerás. Es decir, la resolución de un problema complejo se realiza dividiendo el problema en sub-problemas y a continuación dividir estos subproblemas en otros de nivel más bajo, hasta que pueda ser implementada una solución en la computadora. Este método se conoce técnicamente como diseño descendente (top-down) o modular. El proceso de romper el problema en cada etapa y expresar cada paso en forma más detallada se denomina refinamiento sucesivo. Cada subprograma es resuelto mediante un módulo (subprograma) que tiene un solo punto de entrada y un solo punto de salida. Cualquier programa bien diseñado consta de un programa principal (el módulo de nivel más alto) que llama a subprogramas (módulos de nivel más bajo) que a su vez pueden llamar a otros subprogramas. Los programas estructurados de esta forma se dice que tienen un diseño modular y el método de romper el programa en módulos más pequeños se llama programación modular. Los módulos pueden ser planeados, codificados, comprobados y depurados independientemente (incluso por diferentes programadores) y a continuación combinarlos entre sí. El proceso implica la ejecución de los siguientes pasos hasta que el programa se termina: 1. 2. 3. 4.
Programar un módulo. Comprobar el módulo. Si es necesario, depurar el módulo. Combinar el módulo con los módulos anteriores.
El proceso que convierte los resultados del análisis del problema en un diseño modular con refinamientos sucesivos que permitan una posterior traducción a un lenguaje se denomina diseño del algoritmo. El diseño del algoritmo es independiente del lenguaje de programación en el que se vaya a codificar posteriormente. 1.4.3 Ejemplos Ejemplo 1.4.3.1 Un cliente ejecuta un pedido u una fábrica. La fábrica examina en su banco de datos la ficha del cliente, si el cliente es solvente entonces la empresa acepta el pedido; en caso contrario, rechazará el pedido. Redactar el algoritmo correspondiente. Los pasos del algoritmo son: 1. Inicio. 2. Leer el pedido. 3. Examinar la ficha del cliente. 4. Si el cliente es solvente, aceptar pedido; en caso contrario, rechazar pedido. 5. Fin. Ejemplo 1.4.3.2
Se desea diseñar un algoritmo para saber si un número es primo o no. Análisis: Un número es primo si sólo puede dividirse por sí mismo y por la unidad (es decir, no tiene más divisores que él mismo y la unidad). Por ejemplo, 9, 8, 6, 4, 12, 16,20, etc., no son primos, ya que son divisibles por números distintos a ellos mismos y a la unidad. Así, 9 es divisible por 3, 8 lo es por 2, etc. El algoritmo de resolución del problema pasa por dividir sucesivamente el número por 2, 3, 4, etc. 1. Inicio. 2. Poner X igual a 2 (X = 2, X variable que representa a los divisores del número que se busca N). 3. Dividir N por X (N/X). 4. Si el resultado de N/X es entero, entonces N no es un número primo y bifurcar al punto 7; en caso contrario, continuar el proceso. 5. Suma 1 a X (X X + 1). 6. Si X es igual a N, entonces N es un número primo; en caso contrario, bifurcar al punto 3. 7. Fin. Prueba de Escritorio Por ejemplo, si N es 13 1, los pasos anteriores serían: 1. Inicio. 2. x = 2. 3. 131/X. Como el resultado no es entero, se continúa el proceso. 5. X 2 + 1, luego X = 3. 6. Como X no es 131, se bifurca al punto 3. 3. 131/X resultado no es entero. 5. x 3 + 1, x = 4. 6. Como X no es 131 bifurca al punto 3. 3. 131/X ..., etc. 7. Fin. Ejemplo 1.4.3.3 Realizar la suma de todos los números pares entre 2 y 1000. y NUMERO (variables, serán denominadas más tarde) para representar las sumas sucesivas ( 2 +4 ( 2 + 4 + 6 ) , ( 2 + 4 + 6 + 8 ) , etc. La solución se puede escribir con el siguiente algoritmo: El problema consiste en sumar 2 + 4 + 6 + 8 . . . + 1000 . Utilizaremos las palabras SUMA , 1. Inicio. 2. Establecer SUMA a O. 3. Establecer NUMERO a 2. 4. Sumar NUMERO a SUMA. El resultado será el nuevo valor de la suma (SUMA). 5. Incrementar NUMERO en 2 unidades. 6. Si NUMERO =< 1000 bifurcar al paso 4; en caso contrario, escribir el ultimo valor de SUMA y terminar el proceso. 7. Fin.