Vous êtes sur la page 1sur 41

Compilador

Ir a la navegaciónIr a la búsqueda
Este artículo o sección necesita referencias que aparezcan en una publicación
acreditada.
Este aviso fue puesto el 22 de diciembre de 2017.

«Compilación» redirige aquí. Para otras acepciones, véase recopilación.

Diagrama a bloques de la operación de un buen compilador.

En informática, un compilador es un tipo de traductor que transforma un programa entero de


un lenguaje de programación(llamado código fuente) a otro.1 Usualmente el lenguaje
objetivo es código máquina, aunque también puede ser traducido a un código intermedio
(bytecode) o a texto. A diferencia de los intérpretes, los compiladores reúnen diversos
elementos o fragmentos en una misma unidad (un programa ejecutable o una librería), que
puede ser almacenada y reutilizada. Este proceso de traducción se conoce
como compilación.2
La construcción de un compilador involucra la división del proceso en una serie de fases que
variará con su complejidad. Generalmente estas fases se agrupan en dos tareas: el análisis
del programa fuente y la síntesis del programa objeto.

 Análisis: se trata de la comprobación de la corrección del programa fuente, según la


definición del lenguaje en términos de teoría de lenguajes formales. Incluye las fases
correspondientes al análisis léxico (que consiste en la descomposición del programa
fuente en componentes léxicos), análisis sintáctico (agrupación de los componentes
léxicos en frases gramaticales ) y análisis semántico (comprobación de la validez
semántica de las sentencias aceptadas en la fase de análisis sintáctico).
 Síntesis: su objetivo es la generación de la salida expresada en el lenguaje objeto y suele
estar formado por una o varias combinaciones de fases de generación de código
(normalmente se trata de código intermedio o de código objeto) y de optimización de
código (en las que se busca obtener un programa objetivo lo más eficiente posible, según
su complejidad computacional o complejidad de Kolmogórov: tiempo de ejecución,
espacio durante ejecución, espacio para ser almacenado fuera de ejecución, etc).
Alternativamente, las fases descritas para las tareas de análisis y síntesis se pueden agrupar
en:

 Analizador o front-end: es la parte que analiza el código fuente, comprueba su validez,


genera el árbol de derivación y rellena los valores de la tabla de símbolos. Esta parte
suele ser independiente de la plataforma o sistema para el cual se vaya a compilar, y está
compuesta por las fases comprendidas entre el análisis léxico y la generación de código
intermedio.
 Generador o back-end: es la parte que genera el código máquina, específico de una
plataforma, a partir de los resultados de la fase de análisis, realizada por este generador.
Esta división permite que el mismo generador se utilice para crear el código máquina de
varios lenguajes de programación distintos y que el mismo analizador que sirve para examinar
el código fuente de un lenguaje de programación concreto sirva para producir código
máquina en varias plataformas. Suele incluir la generación y optimización del código
dependiente de la máquina.

Índice

 1Historia
 2Tipos de compiladores
 3Proceso de compilación
 4Etapas del proceso
o 4.1Fase de análisis
 4.1.1Análisis léxico
 4.1.2Análisis sintáctico
 4.1.3Análisis semántico
o 4.2Fase de síntesis
 4.2.1Generación de código intermedio
o 4.3Optimización de código
 5Estructura de datos principales
o 5.1Componentes léxicos o tókenes
o 5.2Árbol sintáctico
o 5.3Tabla de símbolos
o 5.4Tabla de literales
o 5.5Código intermedio
o 5.6Archivos temporales
 6Véase también
 7Referencias
 8Enlaces externos

Historia[editar]
Artículo principal: Historia de la construcción de los compiladores
En 1946 se desarrolló la primera computadora digital. En un principio, estas máquinas
ejecutaban instrucciones consistentes en códigos numéricos que señalaban a los circuitos de
la máquina los estados correspondientes a cada operación, lo que se denominó lenguaje
máquina.
Pronto los primeros usuarios de estos ordenadores descubrieron la ventaja de escribir sus
programas mediante claves más fáciles de recordar que esos códigos; al final, todas esas
claves juntas se traducían manualmente a lenguaje máquina. Estas claves constituyen los
llamados lenguajes ensambladores.
Pese a todo, el lenguaje ensamblador seguía siendo el de una máquina, pero más fácil de
manejar. Los trabajos de investigación se orientaron hacia la creación de un lenguaje que
expresara las distintas acciones a realizar de una manera lo más sencilla posible para una
persona. El primer compilador fue escrito por Grace Hopper, en 1952 para el lenguaje de
programación A-0. En 1950 John Backus dirigió una investigación en IBM sobre un lenguaje
algebraico. En 1954 se empezó a desarrollar un lenguaje que permitía escribir fórmulas
matemáticas de manera traducible por un ordenador; le llamaron FORTRAN (FORmulae
TRANslator). Fue el primer lenguaje de alto nivel y se introdujo en 1957 para el uso de la
computadora IBM modelo 704.
Surgió así por primera vez el concepto de un traductor como un programa que traducía un
lenguaje a otro lenguaje. En el caso particular de que el lenguaje a traducir es un lenguaje de
alto nivel y el lenguaje traducido de bajo nivel, se emplea el término compilador.
El trabajo de realizar un compilador fue complicado de realizar. El primer compilador
de FORTRAN tardó 18 años-persona en realizarse y era muy sencillo. Este desarrollo de
FORTRAN estaba muy influenciado por la máquina objeto en la que iba a ser implementado.
Como un ejemplo de ello tenemos el hecho de que los espacios en blanco fuesen ignorados,
debido a que el periférico que se utilizaba como entrada de programas (una lectora de tarjetas
perforadas) no contaba correctamente los espacios en blanco.
El primer compilador autocontenido, es decir, capaz de compilar su propio código fuente fue el
creado para Lisp por Hart y Levin en el MIT en 1962. Desde 1970 se ha convertido en una
práctica común escribir el compilador en el mismo lenguaje que este compila, aunque
PASCAL y C han sido alternativas muy usadas.
Crear un compilador autocontenido genera un problema llamado bootstrapping, es decir el
primer compilador creado para un lenguaje tiene que o bien ser compilado por un compilador
escrito en otro lenguaje o bien compilado al ejecutar el compilador en un intérprete.

Tipos de compiladores[editar]
Esta taxonomía de los tipos de compiladores no es excluyente, por lo que puede haber
compiladores que se adscriban a varias categorías:

 Compiladores cruzados: generan código para un sistema distinto del que están
funcionando.
 Compiladores optimizadores: realizan cambios en el código para mejorar su eficiencia,
pero manteniendo la funcionalidad del programa original.
 Compiladores de una sola pasada: generan el código máquina a partir de una única
lectura del código fuente.
 Compiladores de varias pasadas: necesitan leer el código fuente varias veces antes de
poder producir el código máquina.
 Compiladores JIT (just in time): forman parte de un intérprete y compilan partes del código
según se necesitan.
En las primeras épocas de la informática, los compiladores eran considerados un software de
los más complejos existentes.
Los primeros compiladores se realizaron programándolos directamente en lenguaje máquina o
en ensamblador. Una vez que se dispone de un compilador, se pueden escribir nuevas
versiones del compilador (u otros compiladores distintos) en el lenguaje que compila ese
compilador.
Actualmente existen herramientas que facilitan la tarea de escribir compiladores
o intérpretes informáticos. Estas herramientas permiten generar el esqueleto del analizador
sintáctico a partir de una definición formal del lenguaje de partida, especificada normalmente
mediante una gramática formal y barata, dejando únicamente al programador del compilador
la tarea de programar las acciones semánticas asociadas.

Proceso de compilación[editar]
Es el proceso por el cual se traducen las instrucciones escritas en un determinado lenguaje de
programación a lenguaje máquina. Además de un traductor, se pueden necesitar otros
programas para crear un programa objeto ejecutable. Un programa fuente se puede dividir en
módulos almacenados en archivos distintos. La tarea de reunir el programa fuente a menudo
se confía a un programa distinto, llamado preprocesador. El preprocesador también puede
expandir abreviaturas, llamadas a macros, a proposiciones del lenguaje fuente.
Normalmente la creación de un programa ejecutable (un típico
archivo .exe para Windows o DOS) conlleva dos pasos. El primer paso se
llama compilación (propiamente dicho) y traduce el código fuente escrito en un lenguaje de
programación almacenado en un archivo a código en bajo nivel (normalmente en código
objeto, no directamente a lenguaje máquina). El segundo paso se llama enlazado en el cual se
enlaza el código de bajo nivel generado de todos los ficheros y subprogramas que se han
mandado a compilar y se añade el código de las funciones que hay en las bibliotecas del
compilador para que el ejecutable pueda comunicarse directamente con el sistema operativo,
traduciendo así finalmente el código objeto a código máquina, y generando un módulo
ejecutable.
Estos dos pasos se pueden hacer por separado, almacenando el resultado de la fase de
compilación en archivos objetos (un típico.obj para Microsoft Windows, DOS o para Unix);
para enlazarlos en fases posteriores, o crear directamente el ejecutable; con lo que la fase de
compilación se almacena solo temporalmente. Un programa podría tener partes escritas en
varios lenguajes (por ejemplo C, C++ y Asm), que se podrían compilar de forma independiente
y luego enlazar juntas para formar un único módulo ejecutable.

Etapas del proceso[editar]


El proceso de traducción se compone internamente de varias etapas o fases, que realizan
distintas operaciones lógicas. Es útil pensar en estas fases como en piezas separadas dentro
del traductor, y pueden en realidad escribirse como operaciones codificadas separadamente
aunque en la práctica a menudo se integren juntas.
Fase de análisis[editar]
Análisis léxico[editar]
Artículo principal: Analizador léxico

El análisis léxico constituye la primera fase, aquí se lee el programa fuente de izquierda a
derecha y se agrupa en componentes léxicos (tókenes), que son secuencias de caracteres
que tienen un significado. Además, todos los espacios en blanco, líneas en blanco,
comentarios y demás información innecesaria se elimina del programa fuente. También se
comprueba que los símbolos del lenguaje (palabras clave, operadores, etc.) se han escrito
correctamente.
Como la tarea que realiza el analizador léxico es un caso especial de coincidencia de
patrones, se necesitan los métodos de especificación y reconocimiento de patrones, se usan
principalmente los autómatas finitos que acepten expresiones regulares. Sin embargo, un
analizador léxico también es la parte del traductor que maneja la entrada del código fuente, y
puesto que esta entrada a menudo involucra un importante gasto de tiempo, el analizador
léxico debe funcionar de manera tan eficiente como sea posible.
Análisis sintáctico[editar]
Artículo principal: Analizador sintáctico

En esta fase los caracteres o componentes léxicos se agrupan jerárquicamente en frases


gramaticales que el compilador utiliza para sintetizar la salida. Se comprueba si lo obtenido de
la fase anterior es sintácticamente correcto (obedece a la gramática del lenguaje). Por lo
general, las frases gramaticales del programa fuente se representan mediante un árbol de
análisis sintáctico.
La estructura jerárquica de un programa normalmente se expresa utilizando reglas recursivas.
Por ejemplo, se pueden dar las siguientes reglas como parte de la definición de expresiones:

1. Cualquier identificador es una expresión.


2. Cualquier número es una expresión.
3. Si expresión1 y expresión2 son expresiones, entonces también lo son:
 expresión1 + expresión2
 expresión1 * expresión2
 ( expresión1 )
Las reglas 1 y 2 son reglas básicas (no recursivas), en tanto que la regla 3 define expresiones
en función de operadores aplicados a otras expresiones.
La división entre análisis léxico y análisis sintáctico es algo arbitraria. Un factor para
determinar la división es si una construcción del lenguaje fuente es inherentemente recursiva o
no. Las construcciones léxicas no requieren recursión, mientras que las construcciones
sintácticas suelen requerirla. No se requiere recursión para reconocer los identificadores, que
suelen ser cadenas de letras y dígitos que comienzan con una letra. Normalmente, se
reconocen los identificadores por el simple examen del flujo de entrada, esperando hasta
encontrar un carácter que no sea ni letra ni dígito, y agrupando después todas las letras y
dígitos encontrados hasta ese punto en un componente léxico llamado identificador. Por otra
parte, esta clase de análisis no es suficientemente poderoso para analizar expresiones o
proposiciones. Por ejemplo, no podemos emparejar de manera apropiada los paréntesis de las
expresiones, o las palabras begin y end en proposiciones sin imponer alguna clase de
estructura jerárquica o de anidamiento a la entrada.
Análisis semántico[editar]
La fase de análisis semántico revisa el programa fuente para tratar de encontrar errores
semánticos y reúne la información sobre los tipos para la fase posterior de generación de
código. En ella se utiliza la estructura jerárquica determinada por la fase de análisis sintáctico
para identificar los operadores y operandos de expresiones y proposiciones.
Un componente importante del análisis semántico es la verificación de tipos. Aquí, el
compilador verifica si cada operador tiene operandos permitidos por la especificación del
lenguaje fuente. Por ejemplo, las definiciones de muchos lenguajes de programación
requieren que el compilador indique un error cada vez que se use un número real como índice
de una matriz. Sin embargo, la especificación del lenguaje puede imponer restricciones a los
operandos, por ejemplo, cuando un operador aritmético binario se aplica a un número entero y
a un número real.3 Revisa que los arreglos tengan definido el tamaño correcto.
Fase de síntesis[editar]
Consiste en generar el código objeto equivalente al programa fuente. Solo se genera código
objeto cuando el programa fuente está libre de errores de análisis, lo cual no quiere decir que
el programa se ejecute correctamente, ya que un programa puede tener errores de concepto o
expresiones mal calculadas. Por lo general el código objeto es código de máquina
relocalizable o código ensamblador. Las posiciones de memoria se seleccionan para cada una
de las variables usadas por el programa. Después, cada una de las instrucciones intermedias
se traduce a una secuencia de instrucciones de máquina que ejecuta la misma tarea. Un
aspecto decisivo es la asignación de variables a registros.
Generación de código intermedio[editar]
Después de los análisis sintáctico y semántico, algunos compiladores generan una
representación intermedia explícita del programa fuente. Se puede considerar esta
representación intermedia como un programa para una máquina abstracta. Esta
representación intermedia debe tener dos propiedades importantes; debe ser fácil de producir
y fácil de traducir al programa objeto.
La representación intermedia puede tener diversas formas. Existe una forma intermedia
llamada «código de tres direcciones» que es como el lenguaje ensamblador de una máquina
en la que cada posición de memoria puede actuar como un registro. El código de tres
direcciones consiste en una secuencia de instrucciones, cada una de las cuales tiene como
máximo tres operandos. Esta representación intermedia tiene varias propiedades:

 Primera: cada instrucción de tres direcciones tiene a lo sumo un operador, además de la


asignación, por tanto, cuando se generan estas instrucciones, el traductor tiene que
decidir el orden en que deben efectuarse las operaciones.
 Segunda: el traductor debe generar un nombre temporal para guardar los valores
calculados por cada instrucción.
 Tercera: algunas instrucciones de «tres direcciones» tienen menos de tres operandos, por
ejemplo, la asignación.
Optimización de código[editar]
Artículo principal: Compilador optimizador

La fase de optimización de código consiste en mejorar el código intermedio, de modo que


resulte un código máquina más rápido de ejecutar. Esta fase de la etapa de síntesis es posible
sobre todo si el traductor es un compilador (difícilmente un intérprete puede optimizar el
código objeto). Hay mucha variación en la cantidad de optimización de código que ejecutan los
distintos compiladores. En los que hacen mucha optimización, llamados «compiladores
optimizadores», una parte significativa del tiempo del compilador se ocupa en esta fase. Sin
embargo, hay optimizaciones sencillas que mejoran sensiblemente el tiempo de ejecución del
programa objeto sin retardar demasiado la compilación.3

Estructura de datos principales[editar]


La interacción entre los algoritmos utilizados por las fases del compilador y las estructuras de
datos que soportan estas fases es, naturalmente, muy fuerte. El escritor del compilador se
esfuerza por implementar estos algoritmos de una manera tan eficaz como sea posible, sin
aumentar demasiado la complejidad. De manera ideal, un compilador debería poder compilar
un programa en un tiempo proporcional al tamaño del mismo.
Componentes léxicos o tókenes[editar]
Cuando un analizador léxico reúne los caracteres en un token, generalmente representa el
token de manera simbólica, es decir, como un valor de un tipo de datos enumerado que
representa el conjunto de tokens del lenguaje fuente. En ocasiones también es necesario
mantener la cadena de caracteres misma u otra información derivada de ella, tal como el
nombre asociado con un token identificador o el valor de un token de número.
En la mayoría de los lenguajes el analizador léxico solo necesita generar un token a la vez. En
este caso se puede utilizar una variable global simple para mantener la información del token.
En otros casos (cuyo ejemplo más notable es FORTRAN), puede ser necesario un arreglo (o
vector) de tókenes.
Árbol sintáctico[editar]
Si el analizador sintáctico genera un árbol sintáctico, por lo regular se construye como una
estructura estándar basada en un puntero que se asigna de manera dinámica a medida que
se efectúa el análisis sintáctico. El árbol entero puede entonces conservarse como una
variable simple que apunta al nodo raíz. Cada nodo en la estructura es un registro cuyos
campos representan la información recolectada tanto por el analizador sintáctico como,
posteriormente, por el analizador semántico. Por ejemplo, el tipo de datos de una expresión
puede conservarse como un campo en el nodo del árbol sintáctico para la expresión.
En ocasiones, para ahorrar espacio, estos campos se asignan de manera dinámica, o se
almacenan en otras estructuras de datos, tales como la tabla de símbolos, que permiten una
asignación y desasignación selectivas. En realidad, cada nodo del árbol sintáctico por sí
mismo puede requerir de atributos diferentes para ser almacenado, de acuerdo con la clase de
estructura del lenguaje que represente. En este caso, cada nodo en el árbol sintáctico puede
estar representado por un registro variable, con cada clase de nodo conteniendo solamente la
información necesaria para ese caso.
Tabla de símbolos[editar]
Esta estructura de datos mantiene la información asociada con los
identificadores: funciones, variables, constantes y tipos de datos. La tabla de símbolos
interactúa con casi todas las fases del compilador: el analizador léxico, el analizador sintáctico
o el analizador semántico pueden introducir identificadores dentro de la tabla; el analizador
semántico agregará tipos de datos y otra información; y las fases de optimización y generación
de código utilizarán la información proporcionada por la tabla de símbolos para efectuar
selecciones apropiadas de código objeto.
Puesto que la tabla de símbolos tendrá solicitudes de acceso con tanta frecuencia, las
operaciones de inserción, eliminación y acceso necesitan ser eficientes, preferiblemente
operaciones de tiempo constante. Una estructura de datos estándar para este propósito es la
tabla de dispersión o de cálculo de dirección, aunque también se pueden utilizar diversas
estructuras de árbol. En ocasiones se utilizan varias tablas y se mantienen en una lista o pila.
Tabla de literales[editar]
La búsqueda y la inserción rápida son esenciales también para la tabla de literales, la cual
almacena constantes y cadenas utilizadas en el programa. Sin embargo, una tabla de literales
necesita impedir las eliminaciones porque sus datos se aplican globalmente al programa y una
constante o cadena aparecerá solo una vez en esta tabla. La tabla de literales es importante
en la reducción del tamaño de un programa en la memoria al permitir la reutilización de
constantes y cadenas. También es necesaria para que el generador de código construya
direcciones simbólicas para las literales y para introducir definiciones de datos en el archivo de
código objeto.
Código intermedio[editar]
De acuerdo con la clase de código intermedio (por ejemplo, código de tres direcciones o
código P) y de las clases de optimizaciones realizadas, este código puede conservarse como
un arreglo de cadenas de texto, un archivo de texto temporal o bien una lista de estructuras
ligadas. En los compiladores que realizan optimizaciones complejas debe ponerse particular
atención a la selección de representaciones que permitan una fácil reorganización.
Generación de código intermedio
Después de los análisis sintáctico y semántico, algunos compiladores generan una
representación intermedia explícita del programa fuente. Se puede considerar esta
representación intermedia como un programa para una máquina abstracta. Esta
representación intermedia debe tener dos propiedades importantes; debe ser fácil de producir
y fácil de traducir al programa objeto.
La representación intermedia puede tener diversas formas. Existe una forma intermedia
llamada «código de tres direcciones», que es como el lenguaje ensamblador para una
máquina en la que cada posición de memoria puede actuar como un registro. El código de tres
direcciones consiste en una secuencia de instrucciones, cada una de las cuales tiene como
máximo tres operandos. El programa fuente de (1) puede aparecer en código de tres
direcciones como

temp1 := entreal(60)
temp2 := id3 * temp1 ===> (2)
temp3 := id2 + temp2
id1 := temp3

Esta representación intermedia tiene varias propiedades. Primera, cada instrucción de tres
direcciones tiene a lo sumo un operador, además de la asignación. Por tanto, cuando se
generan esas instrucciones el compilador tiene que decidir el orden en que deben efectuarse,
las operaciones; la multiplicación precede a la adición al programa fuente de. Segunda, el
compilador debe generar un nombre temporal para guardar los valores calculados por cada
instrucción. Tercera, algunas instrucciones de «tres direcciones» tienen menos de tres
operadores, por ejemplo la primera y la última instrucciones de asignación.
Optimización de Código
La fase de optimización de código trata de mejorar el código intermedio de modo que resulte
un código de máquina más rápido de ejecutar. Algunas optimizaciones son triviales. Por
ejemplo, un algoritmo natural genera el código intermedio (2) utilizando una instrucción para
cada operador de la representación del árbol después del análisis semántico, aunque hay una
forma mejor de realizar los mismos cálculos usando las dos instrucciones

temp1 := id3 * 60.0 ===> (3)


id1 := id2 + temp1

Este sencillo algoritmo no tiene nada de malo, puesto que el problema se puede solucionar en
la fase de optimización de código. Esto es, el compilador puede deducir que la conversión de
60 de entero a real se puede hacer de una vez por todas en el momento de la compilación, de
modo que la operación "entreal( )" se puede eliminar. Además, temp3 se usa solo una vez,
para transmitir su valor a id1. Entonces resulta seguro sustituir a id1 por temp3, a partir de lo
cual la última proposición de (2) no se necesita y se obtiene el código de (3).
Hay muchas variaciones en la cantidad de optimización de código que ejecutan los distintos
compiladores. En lo que hacen mucha optimización llamados «compiladores optimizadores»,
una parte significativa del tiempo del compilador se ocupa en esta fase. Sin embargo, hay
optimizaciones sencillas que mejoran sensiblemente el tiempo de ejecución del programa
objeto sin retardar demasiado la compilación.
Archivos temporales[editar]
Al principio las computadoras no tenían la suficiente memoria para guardar un programa
completo durante la compilación. Este problema se resolvió mediante el uso de archivos
temporales para mantener los productos de los pasos intermedios durante la traducción o bien
al compilar «al vuelo», es decir, manteniendo solo la información suficiente de las partes
anteriores del programa fuente que permita proceder a la traducción.
Las limitaciones de memoria son ahora un problema mucho menor, y es posible requerir que
una unidad de compilación entera se mantenga en memoria, en especial si se dispone de la
compilación por separado en el lenguaje. Con todo, los compiladores ocasionalmente
encuentran útil generar archivos intermedios durante alguna de las etapas del procesamiento.
Algo típico de estos es la necesidad de direcciones de corrección hacia atrás durante la
generación de código.

Véase también[editar]
 BlueJ
 Lenguaje de programación
 Proceso de traducción de programas
 Lenguaje ensamblador
 Ensamblador
 Desensamblador
 Decompilador
 Intérprete
 Depurador
 Lenguaje de alto nivel
 Lenguaje de bajo nivel
 Lenguaje de máquina
 Historia de la construcción de los compiladores

 Libros Principles of Compiler Design, Compilers: Principles, Techniques, and Tools


Lenguaje de programación
Ir a la navegaciónIr a la búsqueda

Captura de la microcomputadora Commodore PET-32 mostrando un programa en el lenguaje de


programación de alto nivel BASIC, bajo el emuladorVICE, en una distribución GNU/Linux.

Un ejemplo de código fuente escrito en el lenguaje de programación Java, que imprimirá el mensaje
"Hello World!" a la salida estándar cuando es compilado y ejecutado

Un lenguaje de programación es un lenguaje formal que proporciona una serie


de instrucciones que permiten a un programador escribir secuencias de órdenes
y algoritmos a modo de controlar el comportamiento físico y lógico de una computadora con el
objetivo de que produzca diversas clases de datos. A todo este conjunto de órdenes y datos
escritos mediante un lenguaje de programación se le conoce como programa.
Algunos de estos lenguajes están formados por un conjunto de símbolos (llamado alfabeto),
reglas gramaticales (léxico/morfológicas y sintácticas) y reglas semánticas, que en conjunto
definen las estructuras válidas en el lenguaje y su significado. Al proceso por el cual se
escribe, se prueba, se depura, se compila (de ser necesario) y se mantiene el código
fuente de un programa informático se le llama programación, que es la que define la creación
de software mediante la aplicación de procedimientos lógicos a través de los siguientes pasos:

 El desarrollo lógico del programa para resolver un problema en particular.


 Escritura de la lógica del programa empleando un lenguaje de programación específico
(codificación del programa).
 Ensamblaje o compilación del programa hasta convertirlo en lenguaje de máquina.
 Prueba y depuración del programa.
 Desarrollo de la documentación.
Existe un error común que trata por sinónimos los términos 'lenguaje de programación' y
'lenguaje informático'. Los lenguajes informáticos engloban a los lenguajes de programación y
a otros más, como por ejemplo HTML (lenguaje para el marcado de páginas web que no es
propiamente un lenguaje de programación, sino un conjunto de instrucciones que permiten
estructurar el contenido de los documentos).
El lenguaje de programación permite especificar de manera precisa sobre qué datos debe
operar un software específico, cómo deben ser almacenados o transmitidos dichos datos, y
qué acciones debe dicho software tomar bajo una variada gama de circunstancias. Todo esto,
a través de un lenguaje que intenta estar relativamente próximo al lenguaje humano o natural.
Una característica relevante de los lenguajes de programación es precisamente que más de
un programador pueda usar un conjunto común de instrucciones que sean comprendidas
entre ellos para realizar la construcción de un programa de forma colaborativa.

Índice

 1Historia
 2Clasificación de los lenguajes de programación
o 2.1Clasificación histórica o por generaciones
 3Paradigma de programación
 4Clasificación por paradigmas
 5Elementos
o 5.1Variables y vectores
o 5.2Condicionales
o 5.3Bucles
o 5.4Funciones
o 5.5Sintaxis
o 5.6Semántica estática
o 5.7Sistema de tipos
 5.7.1Lenguajes tipados versus lenguajes no tipados
 5.7.2Tipos estáticos versus tipos dinámicos
 5.7.3Tipos débiles y tipos fuertes
 6Implementación
 7Técnica
o 7.1Paradigmas
 8Véase también
 9Referencias
 10Enlaces externos

Historia[editar]
Artículo principal: Historia de los lenguajes de programación

Código Fortran en una tarjeta perforada, mostrando el uso especializado de las columnas 1-5, 6 y 73-80.

Para que la computadora entienda nuestras instrucciones debe usarse un lenguaje específico
conocido como código máquina, que la máquina lee fácilmente, pero que es excesivamente
complicado para las personas. De hecho solo consiste en cadenas extensas de números 0 y
1.
Para facilitar el trabajo, los primeros operadores de computadoras decidieron crear un
traductor para reemplazar los 0 y 1 por palabras o abstracción de palabras y letras
provenientes del inglés; este se conoce como lenguaje ensamblador. Por ejemplo, para sumar
se usa la letra A de la palabra inglesa add (sumar). El lenguaje ensamblador sigue la misma
estructura del lenguaje máquina, pero las letras y palabras son más fáciles de recordar y
entender que los números.
La necesidad de recordar secuencias de programación para las acciones usuales llevó a
denominarlas con nombres fáciles de memorizar y
asociar: ADD (sumar), SUB (restar), MUL (multiplicar), CALL (ejecutar subrutina), etc. A esta
secuencia de posiciones se le denominó "instrucciones", y a este conjunto de instrucciones se
le llamó lenguaje ensamblador. Posteriormente aparecieron diferentes lenguajes de
programación, los cuales reciben su denominación porque tienen una
estructura sintáctica semejante a la de los lenguajes escritos por los humanos, denominados
también lenguajes de alto nivel.
El primer programador de computadora conocido fue una mujer: Ada Lovelace, hija
de Anabella Milbanke Byron y Lord Byron. Anabella inició en las matemáticas a Ada quien,
después de conocer a Charles Babbage, tradujo y amplió una descripción de su máquina
analítica. Incluso aunque Babbage nunca completó la construcción de cualquiera de sus
máquinas, el trabajo que Ada realizó con estas le hizo ganarse el título de primera
programadora de computadoras del mundo. El nombre del lenguaje de programación Ada fue
escogido como homenaje a esta programadora.
A finales de 1953, John Backus sometió una propuesta a sus superiores en IBM para
desarrollar una alternativa más práctica al lenguaje ensamblador para programar
la computadora central IBM 704. El histórico equipo Fortran de Backus consistió en los
programadores Richard Goldberg, Sheldon F. Best, Harlan Herrick, Peter Sheridan, Roy
Nutt, Robert Nelson, Irving Ziller, Lois Haibt y David Sayre.1
El primer manual para el lenguaje Fortran apareció en octubre de 1956, con el
primer compilador Fortran entregado en abril de 1957. Esto era un compilador optimizado,
porque los clientes eran reacios a usar un lenguaje de alto nivel a menos que su compilador
pudiera generar código cuyo desempeño fuera comparable al de un código hecho a mano en
lenguaje ensamblador.
En 1960, se creó COBOL, uno de los lenguajes usados aún en la actualidad, en informática de
gestión.
A medida que la complejidad de las tareas que realizaban las computadoras aumentaba, se
hizo necesario disponer de un método más eficiente para programarlas. Entonces, se crearon
los lenguajes de alto nivel, como lo fue BASIC en las versiones introducidas en los
microordenadores de la década de 1980. Mientras que una tarea tan sencilla como sumar dos
números puede necesitar varias instrucciones en lenguaje ensamblador, en un lenguaje de
alto nivel bastará una sola sentencia.

Clasificación de los lenguajes de programación[editar]


Los lenguajes de programación han sido históricamente clasificados atendiendo a distintos
criterios:

 Clasificación histórica
A medida que surgian nuevos lenguajes que permitían nuevos estilos de programación
más expresiva, se distinguieron dichos estilos en una serie de generaciones, cada
una representando lenguajes de programación surgidos en una época similar y con
características genéricas comunes.

 Lenguajes de alto y de bajo nivel


Los lenguajes de programación se suelen clasificar dentro de dos amplias categorías
que se refieren a su "nivel de abstracción", es decir, en cuanto a lo específico o
general que es respecto a la arquitectura de computación inherente al sistema que se
está utilizando.

 Clasificación por paradigmas


Los paradigmas de programación distinguen distintos modelos de cómputo y de estilos
de estructurar y organizar las tareas que debe realizar un programa. Un lenguaje de
programación puede ofrecer soporte a uno o varios paradigmas de programación, total
o parcialmente.

 Clasificación por propósito


Se distinguen los lenguajes de programación de propósito general de aquellos
de propósito específico.
En algunas ocasiones los lenguajes de programación son también
clasificados en familias que comparten ciertas características comunes como
el estilo general de la sintaxisque emplean. Habitualmente estas
características suelen ser heredadas de lenguajes de programación
anteriores que sirvieron de inspiración a los creadores de dicho lenguaje.
Clasificación histórica o por generaciones[editar]
Los equipos de ordenador (el hardware) han pasado por cuatro generaciones,
de las que las tres primeras (ordenadores con válvulas, transistores y circuitos
integrados) están muy claras, la cuarta (circuitos integrados a gran escala) es
más discutible.
Algo parecido ha ocurrido con la programación de los ordenadores
(el software), que se realiza en lenguajes que suelen clasificarse en cinco
generaciones, de las que las tres primeras son evidentes, mientras no todo el
mundo está de acuerdo en las otras dos. Estas generaciones no coincidieron
exactamente en el tiempo con las de hardware, pero sí de forma aproximada,
y son las siguientes:

 Primera Generación: Los primeros ordenadores se programaban


directamente en código de máquina (basado en sistema binario), que
puede representarse mediante secuencias de 0 y 1. No obstante, cada
modelo de ordenador tiene su propia estructura interna a la hora de
programarse. A estos lenguajes se les denominaba Lenguaje de bajo
nivel, porque sus instrucciones ejercen un control directo sobre el
hardware y están condicionados por la estructura física de las
computadoras que lo soportan. Dado que este tipo de lenguaje se acerca
mucho más a la lógica de la máquina que a la humana, es mucho más
complicado programar con él. El uso de la palabra bajo en su
denominación no implica que el lenguaje sea menos potente que
un lenguaje de alto nivel, sino que se refiere a la
reducida abstracción entre el lenguaje y el hardware. Por ejemplo, se
utiliza este tipo de lenguajes para programar tareas críticas de
los sistemas operativos, de aplicaciones en tiempo real o controladores
de dispositivos. Otra limitación de estos lenguajes es que se requiere de
ciertos conocimientos de programación para realizar las secuencias de
instrucciones lógicas.

 Segunda generación: Los lenguajes simbólicos, asimismo propios de la


máquina, simplifican la escritura de las instrucciones y las hacen más
legibles. Se refiere al lenguaje ensamblador ensamblado a través de un
macroensamblador. Es el lenguaje de máquina combinado con una serie
de poderosas macros que permiten declarar estructuras de datos y de
control complejas.

 Tercera Generación: Los lenguajes de alto nivel sustituyen las


instrucciones simbólicas por códigos independientes de la máquina,
parecidas al lenguaje humano o al de las Matemáticas. Se crearon para
que el usuario común pudiese solucionar un problema de procesamiento
de datos de una manera más fácil y rápida. Son usados en ámbitos
computacionales donde se logra un alto rendimiento con respecto a
lenguajes de generaciones anteriores. Entre ellos se
encuentran C, Fortran, Smalltalk, Ada, C++, C#, Cobol, Delphi, Java y PH
P, entre otros. Algunos de estos lenguajes pueden ser de propósito
general, es decir, que el lenguaje no está enfocado a una única
especialidad, sino que puede usarse para crear todo tipo de programas
Para ciertas tareas más comunes, existen bibliotecas para facilitar la
programación que permiten la reutilización de código.

 Cuarta generación: se ha dado este nombre a ciertas herramientas que


permiten construir aplicaciones sencillas combinando piezas
prefabricadas. Hoy se piensa que estas herramientas no son,
propiamente hablando, lenguajes. Cabe mencionar que, algunos
proponen reservar el nombre de cuarta generación para la programación
orientada a objetos. Estos últimos tienen una estructura muy parecida al
idioma inglés. Algunas de sus características son: acceso a base de
datos, capacidades gráficas, generación de código automáticamente, así
como poder programar visualmente (como por ejemplo Visual
Basic o SQL). Entre sus ventajas se cuenta una mayor productividad y
menor agotamiento del programador, así como menor concentración por
su parte, ya que las herramientas proporcionadas incluyen secuencias de
instrucciones. El nivel de concentración que se requiere es menor, ya que
algunas instrucciones, que le son dadas a las herramientas, a su vez,
engloban secuencias de instrucciones a otro nivel dentro de la
herramienta. Cuando hay que dar mantenimiento a los programas
previamente elaborados, es menos complicado por requerir menor nivel
de concentración. Por otro lado, sus desventajas consisten en que estas
herramientas prefabricadas son generalmente menos flexibles que las
instrucciones directas en los lenguajes de bajo nivel. Además, se suelen
crear dependencias con uno o varios proveedores externos, lo que se
traduce en pérdida de autonomía. Asimismo, es frecuente que dichas
herramientas prefabricadas contengan librerías de otros proveedores, que
conlleva instalar opciones adicionales que son consideradas opcionales.
A menos que existan acuerdos con otros proveedores, son programas
que se ejecutan únicamente con el lenguaje que lo creó. Tampoco suelen
cumplir con los estándares internacionales ISO y ANSI, lo cual conlleva
un riesgo futuro por desconocerse su tiempo de permanencia en el
mercado. Algunos ejemplos son: NATURAL y PL/SQL.

 Quinta generación: en ocasiones se llama así a los lenguajes


de inteligencia artificial, aunque con el fracaso del proyecto japonés de la
quinta generación esta denominación ha caído en desuso.

Paradigma de programación[editar]
Un paradigma de programación consiste en un método para llevar a cabo
cómputos y la forma en la que deben estructurarse y organizarse las tareas
que debe realizar un programa.2 Se trata de una propuesta tecnológica
adoptada por una comunidad de programadores, y desarrolladores cuyo
núcleo central es incuestionable en cuanto que únicamente trata de resolver
uno o varios problemas claramente delimitados; la resolución de estos
problemas debe suponer consecuentemente un avance significativo en al
menos un parámetro que afecte a la ingeniería de software. Representa un
enfoque particular o filosofía para diseñar soluciones. Los paradigmas difieren
unos de otros, en los conceptos y la forma de abstraer los elementos
involucrados en un problema, así como en los pasos que integran su solución
del problema, en otras palabras, el cómputo. Tiene una estrecha relación con
la formalización de determinados lenguajes en su momento de definición. Es
un estilo de programación empleado.
Un paradigma de programación está delimitado en el tiempo en cuanto a
aceptación y uso, porque nuevos paradigmas aportan nuevas o mejores
soluciones que lo sustituyen parcial o totalmente.
El paradigma de programación que actualmente es más utilizado es la
"orientación a objetos" (OO). El núcleo central de este paradigma es la unión
de datos y procesamiento en una entidad llamada "objeto", relacionable a su
vez con otras entidades "objeto".
Tradicionalmente, datos y procesamiento se han separado en áreas diferente
del diseño y la implementación de software. Esto provocó que grandes
desarrollos tuvieran problemas de fiabilidad, mantenimiento, adaptación a los
cambios y escalabilidad. Con la OO y características como el encapsulado,
polimorfismo o la herencia, se permitió un avance significativo en el desarrollo
de software a cualquier escala de producción. La OO parece estar ligada en
sus orígenes con lenguajes como Lisp y Simula, aunque el primero que acuñó
el título de "programación orientada a objetos" fue Smalltalk.

Clasificación por paradigmas[editar]


En general, la mayoría de paradigmas son variantes de los dos tipos
principales de programación, imperativa y declarativa. En la programación
imperativa se describe paso a paso un conjunto de instrucciones que deben
ejecutarse para variar el estado del programa y hallar la solución, es decir,
un algoritmo en el que se describen los pasos necesarios para solucionar el
problema.
En la programación declarativa las sentencias que se utilizan lo que hacen es
describir el problema que se quiere solucionar; se programa diciendo lo que
se quiere resolver a nivel de usuario, pero no las instrucciones necesarias
para solucionarlo. Esto último se realizará mediante mecanismos internos
de inferencia de información a partir de la descripción realizada.
A continuación se describen algunas de las distintas variantes de paradigmas
de programación:

 Programación imperativa o por procedimientos: es el más usado en


general, se basa en dar instrucciones al ordenador de como hacer las
cosas en forma de algoritmos, en lugar de describir el problema o la
solución. Las recetas de cocina y las listas de revisión de procesos, a
pesar de no ser programas de computadora, son también conceptos
familiares similares en estilo a la programación imperativa; donde cada
paso es una instrucción. Es la forma de programación más usada y la
más antigua, el ejemplo principal es el lenguaje de máquina. Ejemplos de
lenguajes puros de este paradigma serían el C, BASIC o Pascal.

 Programación orientada a objetos: está basada en el imperativo, pero


encapsula elementos denominados objetos que incluyen tanto variables
como funciones. Está representado por C++, C#, Java o Python entre
otros, pero el más representativo sería el Smalltalk que está
completamente orientado a objetos.

 Programación dinámica: está definida como el proceso de romper


problemas en partes pequeñas para analizarlos y resolverlos de forma lo
más cercana al óptimo, busca resolver problemas en O(n) sin usar por
tanto métodos recursivos. Este paradigma está más basado en el modo
de realizar los algoritmos, por lo que se puede usar con cualquier
lenguaje imperativo.

 Programación dirigida por eventos: la programación dirigida por eventos


es un paradigma de programación en el que tanto la estructura como la
ejecución de los programas van determinados por los sucesos que
ocurran en el sistema, definidos por el usuario o que ellos mismos
provoquen.

 Programación declarativa: está basada en describir el problema


declarando propiedades y reglas que deben cumplirse, en lugar de
instrucciones. Hay lenguajes para la programación funcional,
la programación lógica, o la combinación lógico-funcional. La solución es
obtenida mediante mecanismos internos de control, sin especificar
exactamente cómo encontrarla (tan solo se le indica a
la computadora qué es lo que se desea obtener o qué es lo que se está
buscando). No existen asignaciones destructivas, y las variables son
utilizadas con transparencia referencial. Los lenguajes declarativos tienen
la ventaja de ser razonados matemáticamente, lo que permite el uso de
mecanismos matemáticos para optimizar el rendimiento de los
programas.3 Unos de los primeros lenguajes funcionales
fueron Lisp y Prolog.

 Programación funcional: basada en la definición los predicados y es de


corte más matemático, está representado por Scheme (una variante
de Lisp) o Haskell. Pythontambién representa este paradigma.4
 Programación lógica: basado en la definición de relaciones lógicas, está
representado por Prolog.

 Programación con restricciones: similar a la lógica usando ecuaciones.


Casi todos los lenguajes son variantes del Prolog.

 Programación multiparadigma: es el uso de dos o más paradigmas


dentro de un programa. El lenguaje Lisp se considera multiparadigma. Al
igual que Python, que es orientado a
objetos, reflexivo, imperativo y funcional.4 Según lo describe Bjarne
Stroustrup, esos lenguajes permiten crear programas usando más de un
estilo de programación. El objetivo en el diseño de estos lenguajes es
permitir a los programadores utilizar el mejor paradigma para cada
trabajo, admitiendo que ninguno resuelve todos los problemas de la forma
más fácil y eficiente posible. Por ejemplo, lenguajes de programación
como C++, Genie, Delphi, Visual Basic, PHP o D5 combinan el paradigma
imperativo con la orientación a objetos. Incluso existen lenguajes
multiparadigma que permiten la mezcla de forma natural, como en el caso
de Oz, que tiene subconjuntos (particularidad de los lenguajes lógicos), y
otras características propias de lenguajes de programación funcional y
de orientación a objetos. Otro ejemplo son los lenguajes
como Scheme de paradigma funcional o Prolog (paradigma lógico), que
cuentan con estructuras repetitivas, propias del paradigma imperativo.

 Programación reactiva: Este paradigma se basa en la declaración de una


serie de objetos emisores de eventos asíncronos y otra serie de objetos
que se "suscriben" a los primeros (es decir, quedan a la escucha de la
emisión de eventos de estos) y *reaccionan* a los valores que reciben. Es
muy común usar la librería Rx de Microsoft (Acrónimo de Reactive
Extensions), disponible para múltiples lenguajes de programación.

 Lenguaje específico del dominio o DSL: se denomina así a los lenguajes


desarrollados para resolver un problema específico, pudiendo entrar
dentro de cualquier grupo anterior. El más representativo sería SQL para
el manejo de las bases de datos, de tipo declarativo, pero los hay
imperativos, como el Logo.

Elementos[editar]
Variables y vectores[editar]
Artículo principal: Variable (programación)
Imagen tomada de Pauscal, lenguaje de programación en español creado en
Argentina.

Las variables son títulos asignados a espacios en memoria para almacenar


datos específicos. Son contenedores de datos y por ello se diferencian según
el tipo de dato que son capaces de almacenar. En la mayoría de lenguajes de
programación se requiere especificar un tipo de variable concreto para
guardar un dato específico. Por ejemplo, en Java, si deseamos guardar una
cadena de texto debemos especificar que la variable es del tipo String. Por
otra parte, en lenguajes como PHP o JavaScript este tipo de especificación de
variables no es necesario. Además, existen variables compuestas llamadas
vectores. Un vector no es más que un conjunto de bytes consecutivas en
memoria y del mismo tipo guardadas dentro de una variable contenedor. A
continuación, un listado con los tipos de variables y vectores más comunes:

Tipo de
Breve descripción
dato

Estas variables contienen un único carácter, es decir, una letra, un signo


Char
o un número.

Int Contienen un número entero.

Float Contienen un número decimal.

Contienen cadenas de texto, o lo que es lo mismo, es un vector con


String
varias variables del tipo Char.

Boolean Solo pueden contener un cero o un uno.


En el caso de variables booleanas, el cero es considerado para muchos
lenguajes como el literal falso ("False"), mientras que el uno se considera
verdadero ("True").
Condicionales[editar]
Las sentencias condicionales son estructuras de código que indican que, para
que cierta parte del programa se ejecute, deben cumplirse ciertas premisas;
por ejemplo: que dos valores sean iguales, que un valor exista, que un valor
sea mayor que otro... Estos condicionantes por lo general solo se ejecutan
una vez a lo largo del programa. Los condicionantes más conocidos y
empleados en programación son:

 If: Indica una condición para que se ejecute una parte del programa.
 Else if: Siempre va precedido de un "If" e indica una condición para que
se ejecute una parte del programa siempre que no cumpla la condición
del if previo y sí se cumpla con la que el "else if" especifique.
 Else: Siempre precedido de "If" y en ocasiones de "Else If". Indica que
debe ejecutarse cuando no se cumplan las condiciones previas.
Bucles[editar]
Los bucles son parientes cercanos de los condicionantes, pero ejecutan
constantemente un código mientras se cumpla una determinada condición.
Los más frecuentes son:

 For: Ejecuta un código mientras una variable se encuentre entre 2


determinados parámetros.
 While: Ejecuta un código mientras que se cumpla la condición que
solicita.
Hay que decir que a pesar de que existan distintos tipos de bucles, todos son
capaces de realizar exactamente las mismas funciones. El empleo de uno u
otro depende, por lo general, del gusto del programador.
Funciones[editar]
Las funciones se crearon para evitar tener que repetir constantemente
fragmentos de código. Una función podría considerarse como una variable
que encierra código dentro de si. Por lo tanto cuando accedemos a dicha
variable (la función) en realidad lo que estamos haciendo es ordenar al
programa que ejecute un determinado código predefinido anteriormente.
Todos los lenguajes de programación tienen algunos elementos de formación
primitivos para la descripción de los datos y de los procesos o
transformaciones aplicadas a estos datos (tal como la suma de dos números
o la selección de un elemento que forma parte de una colección). Estos
elementos primitivos son definidos por reglas sintácticas y semánticas que
describen su estructura y significado respectivamente.
Sintaxis[editar]
Con frecuencia se resaltan los elementos de la sintaxis con colores diferentes
para facilitar su lectura. Este ejemplo está escrito en Python.

A la forma visible de un lenguaje de programación se la conoce como sintaxis.


La mayoría de los lenguajes de programación son puramente textuales, es
decir, utilizan secuencias de texto que incluyen palabras, números y
puntuación, de manera similar a los lenguajes naturales escritos. Por otra
parte, hay algunos lenguajes de programación que son más gráficos en su
naturaleza, utilizando relaciones visuales entre símbolos para especificar un
programa.
La sintaxis de un lenguaje de programación describe las combinaciones
posibles de los símbolos que forman un programa sintácticamente correcto. El
significado que se le da a una combinación de símbolos es manejado por su
semántica (ya sea formal o como parte del código duro de la referencia de
implementación). Dado que la mayoría de los lenguajes son textuales, este
artículo trata de la sintaxis textual.
La sintaxis de los lenguajes de programación es definida generalmente
utilizando una combinación de expresiones regulares(para la estructura
léxica) y la Notación de Backus-Naur (para la estructura gramática). Este es
un ejemplo de una gramática simple, tomada del lenguaje Lisp:

expresión ::= átomo | lista


átomo ::= número | símbolo
número ::= [+-]? ['0'-'9']+
símbolo ::= ['A'-'Z'] ['a'-'z'].*
lista ::= '(' expresión* ')'

Con esta gramática se especifica lo siguiente:

 una expresión puede ser un átomo o una lista;


 un átomo puede ser un número o un símbolo;
 un número es una secuencia continua de uno o más dígitos decimales,
precedido opcionalmente por un signo más o un signo menos;
 un símbolo es una letra seguida de cero o más caracteres (excluyendo
espacios); y
 una lista es un par de paréntesis que abren y cierran, con cero o más
expresiones en medio.
Algunos ejemplos de secuencias bien formadas de acuerdo a esta gramática:
' 12345 ', ' () ', ' (a b c232 (1)) '
No todos los programas sintácticamente correctos son semánticamente
correctos. Muchos programas sintácticamente correctos tienen
inconsistencias con las reglas del lenguaje; y pueden (dependiendo de la
especificación del lenguaje y la solidez de la implementación) resultar en un
error de traducción o ejecución. En algunos casos, tales programas pueden
exhibir un comportamiento indefinido. Además, incluso cuando un programa
está bien definido dentro de un lenguaje, todavía puede tener un significado
que no es el que la persona que lo escribió estaba tratando de construir.
Usando el lenguaje natural, por ejemplo, puede no ser posible asignarle
significado a una oración gramaticalmente válida o la oración puede ser falsa:

 "Las ideas verdes y descoloridas duermen furiosamente" es una oración


bien formada gramaticalmente pero no tiene significado comúnmente
aceptado.
 "Juan es un soltero casado" también está bien formada gramaticalmente
pero expresa un significado que no puede ser verdadero.
El siguiente fragmento en el lenguaje C es sintácticamente correcto, pero
ejecuta una operación que no está definida semánticamente (dado que p es
un apuntador nulo, las operaciones p->real y p->im no tienen ningún
significado):

complex *p = NULL;
complex abs_p = sqrt (p->real * p->real + p->im * p->im);

Si la declaración de tipo de la primera línea fuera omitida, el programa


dispararía un error de compilación, pues la variable "p" no estaría definida.
Pero el programa sería sintácticamente correcto todavía, dado que las
declaraciones de tipo proveen información semántica solamente.
La gramática necesaria para especificar un lenguaje de programación puede
ser clasificada por su posición en la Jerarquía de Chomsky. La sintaxis de la
mayoría de los lenguajes de programación puede ser especificada utilizando
una gramática Tipo-2, es decir, son gramáticas libres de contexto. Algunos
lenguajes, incluyendo a Perl y a Lisp, contienen construcciones que permiten
la ejecución durante la fase de análisis. Los lenguajes que permiten
construcciones que permiten al programador alterar el comportamiento de un
analizador hacen del análisis de la sintaxis un problema sin decisión única, y
generalmente oscurecen la separación entre análisis y ejecución. En
contraste con el sistema de macros de Lisp y los bloques BEGIN de Perl, que
pueden tener cálculos generales, las macros de C son meros reemplazos de
cadenas, y no requieren ejecución de código.
Semántica estática[editar]
La semántica estática define las restricciones sobre la estructura de los textos
válidos que resulta imposible o muy difícil expresar mediante formalismos
sintácticos estándar. Para los lenguajes compilados, la semántica estática
básicamente incluye las reglas semánticas que se pueden verificar en el
momento de compilar. Por ejemplo el chequeo de que cada identificador sea
declarado antes de ser usado (en lenguajes que requieren tales
declaraciones) o que las etiquetas en cada brazo de una estructura case sean
distintas. Muchas restricciones importantes de este tipo, como la validación de
que los identificadores sean usados en los contextos apropiados (por ejemplo
no sumar un entero al nombre de una función), o que las llamadas a
subrutinas tengan el número y tipo de parámetros adecuado, pueden ser
implementadas definiéndolas como reglas en una lógica conocida como
sistema de tipos. Otras formas de análisis estáticos, como los análisis de flujo
de datos, también pueden ser parte de la semántica estática. Otros lenguajes
de programación como Java y C# tienen un análisis definido de asignaciones,
una forma de análisis de flujo de datos, como parte de su semántica estática.
Sistema de tipos[editar]
Artículo principal: Sistema de tipos

Un sistema de tipos de datos define la manera en la cual un lenguaje de


programación clasifica los valores y expresiones en tipos, cómo pueden ser
manipulados dichos tipos y cómo interactúan. El objetivo de un sistema de
tipos es verificar y normalmente poner en vigor un cierto nivel de exactitud en
programas escritos en el lenguaje en cuestión, detectando ciertas
operaciones inválidas. Cualquier sistema de tipos decidible tiene sus ventajas
y desventajas: mientras por un lado rechaza muchos programas incorrectos,
también prohíbe algunos programas correctos aunque poco comunes. Para
poder minimizar esta desventaja, algunos lenguajes incluyen lagunas de
tipos, conversiones explícitas no verificadas que pueden ser usadas por el
programador para permitir explícitamente una operación normalmente no
permitida entre diferentes tipos. En la mayoría de los lenguajes con tipos, el
sistema de tipos es usado solamente para verificar los tipos de los programas,
pero varios lenguajes, generalmente funcionales, llevan a cabo lo que se
conoce como inferencia de tipos, que le quita al programador la tarea de
especificar los tipos. Al diseño y estudio formal de los sistemas de tipos se le
conoce como teoría de tipos.
Lenguajes tipados versus lenguajes no tipados[editar]
Se dice que un lenguaje es tipado si la especificación de cada operación debe
definir los tipos de datos para los cuales es aplicable, con la implicación de
que no es aplicable a otros tipos. Por ejemplo, " este texto entre
comillas " es una cadena de caracteres. En la mayoría de los lenguajes de
programación, dividir un número por una cadena de caracteres no tiene
ningún significado. Por tanto, la mayoría de los lenguajes de programación
modernos rechazarían cualquier intento de ejecutar dicha operación por parte
de algún programa. En algunos lenguajes, estas operaciones sin significado
son detectadas cuando el programa es compilado (validación de tipos
"estática") y son rechazadas por el compilador, mientras en otros son
detectadas cuando el programa es ejecutado (validación de tipos "dinámica")
y se genera una excepción en tiempo de ejecución.
Un caso especial de lenguajes de tipo son los lenguajes de tipo sencillo.
Estos son con frecuencia lenguajes de marcado o de scripts,
como REXX o SGML, y solamente cuentan con un tipo de datos;
comúnmente cadenas de caracteres que luego son usadas tanto para datos
numéricos como simbólicos.
En contraste, un lenguaje sin tipos, como la mayoría de los lenguajes
ensambladores, permiten que cualquier operación se aplique a cualquier dato,
que por lo general se consideran secuencias de bits de varias longitudes.
Lenguajes de alto nivel sin datos incluyen BCPL y algunas variedades
de Forth.
En la práctica, aunque pocos lenguajes son considerados con tipo desde el
punto de vista de la teoría de tipos (es decir, que verifican o
rechazan todas las operaciones), la mayoría de los lenguajes modernos
ofrecen algún grado de manejo de tipos. Si bien muchos lenguajes de
producción proveen medios para evitar o rodear el sistema de tipado.
Tipos estáticos versus tipos dinámicos[editar]
En lenguajes con tipos estáticos se determina el tipo de todas las expresiones
antes de la ejecución del programa (típicamente al compilar). Por ejemplo, 1 y
(2+2) son expresiones enteras; no pueden ser pasadas a una función que
espera una cadena, ni pueden guardarse en una variable que está definida
como fecha.
Los lenguajes con tipos estáticos pueden manejar tipos explícitos o
tipos inferidos. En el primer caso, el programador debe escribir los tipos en
determinadas posiciones textuales. En el segundo caso, el
compilador infiere los tipos de las expresiones y las declaraciones de acuerdo
al contexto. La mayoría de los lenguajes populares con tipos estáticos, tales
como C++, C# y Java, manejan tipos explícitos. Inferencia total de los tipos
suele asociarse con lenguajes menos populares, tales como Haskell y ML. Sin
embargo, muchos lenguajes de tipos explícitos permiten inferencias parciales
de tipo; tanto Java y C#, por ejemplo, infieren tipos en un número limitado de
casos.
Los lenguajes con tipos dinámicos determinan la validez de los tipos
involucrados en las operaciones durante la ejecución del programa. En otras
palabras, los tipos están asociados con valores en ejecución en lugar
de expresiones textuales. Como en el caso de lenguajes con tipos inferidos,
los lenguajes con tipos dinámicos no requieren que el programador escriba
los tipos de las expresiones. Entre otras cosas, esto permite que una misma
variable se pueda asociar con valores de tipos distintos en diferentes
momentos de la ejecución de un programa. Sin embargo, los errores de tipo
no pueden ser detectados automáticamente hasta que se ejecuta el código,
dificultando la depuración de los programas, no obstante, en lenguajes con
tipos dinámicos se suele dejar de lado la depuración en favor de técnicas de
desarrollo como por ejemplo BDD y TDD. Ruby, Lisp, JavaScript y Python son
lenguajes con tipos dinámicos.
Tipos débiles y tipos fuertes[editar]
Los lenguajes débilmente tipados permiten que un valor de un tipo pueda ser
tratado como de otro tipo, por ejemplo una cadena puede ser operada como
un número. Esto puede ser útil a veces, pero también puede permitir ciertos
tipos de fallas que no pueden ser detectadas durante la compilación o a veces
ni siquiera durante la ejecución.
Los lenguajes fuertemente tipados evitan que pase lo anterior. Cualquier
intento de llevar a cabo una operación sobre el tipo equivocado dispara un
error. A los lenguajes con tipos fuertes se les suele llamar de tipos seguros.
Lenguajes con tipos débiles como Perl y JavaScript permiten un gran número
de conversiones de tipo implícitas. Por ejemplo en JavaScript la expresión 2
* x convierte implícitamente x a un número, y esta conversión es exitosa
inclusive cuando x es null , undefined , un Array o una cadena de letras.
Estas conversiones implícitas son útiles con frecuencia, pero también pueden
ocultar errores de programación.
Las características de estáticos y fuertes son ahora generalmente
consideradas conceptos ortogonales, pero su trato en diferentes textos varía.
Algunos utilizan el término de tipos fuertes para referirse a tipos fuertemente
estáticos o, para aumentar la confusión, simplemente como equivalencia
de tipos estáticos. De tal manera que C ha sido llamado tanto lenguaje de
tipos fuertes como lenguaje de tipos estáticos débiles.

Implementación[editar]

Código fuente de un programa escrito en el lenguaje de programación Java.

La implementación de un lenguaje es la que provee una manera de que se


ejecute un programa para una determinada combinación
de software y hardware. Existen básicamente dos maneras de implementar
un lenguaje: compilación e interpretación.

 Compilación: es el proceso que traduce un programa escrito en un


lenguaje de programación a otro lenguaje de programación, generando
un programa equivalente que la máquina será capaz de interpretar. Los
programas traductores que pueden realizar esta operación se
llaman compiladores. Estos, como los programas ensambladores
avanzados, pueden generar muchas líneas de código de máquina por
cada proposición del programa fuente.
 Interpretación: es una asignación de significados a las fórmulas bien
formadas de un lenguaje formal. Como los lenguajes formales pueden
definirse en términos puramente sintácticos, sus fórmulas bien formadas
pueden no ser más que cadenas de símbolos sin ningún significado. Una
interpretación otorga significado a esas fórmulas.
Se puede también utilizar una alternativa para traducir lenguajes de alto nivel.
En lugar de traducir el programa fuente y grabar en forma permanente el
código objeto que se produce durante la compilación para utilizarlo en una
ejecución futura, el programador solo carga el programa fuente en la
computadora junto con los datos que se van a procesar. A continuación, un
programa intérprete, almacenado en el sistema operativo del disco, o incluido
de manera permanente dentro de la máquina, convierte cada proposición del
programa fuente en lenguaje de máquina conforme vaya siendo necesario
durante el procesamiento de los datos. El código objeto no se graba para
utilizarlo posteriormente.
La siguiente vez que se utilice una instrucción, se la deberá interpretar otra
vez y traducir a lenguaje máquina. Por ejemplo, durante el procesamiento
repetitivo de los pasos de un ciclo o bucle, cada instrucción del bucle tendrá
que volver a ser interpretada en cada ejecución repetida del ciclo, lo cual hace
que el programa sea más lento en tiempo de ejecución (porque se va
revisando el código en tiempo de ejecución) pero más rápido en tiempo de
diseño (porque no se tiene que estar compilando a cada momento el código
completo). El intérprete elimina la necesidad de realizar una compilación
después de cada modificación del programa cuando se quiere agregar
funciones o corregir errores; pero es obvio que un programa objeto compilado
con antelación deberá ejecutarse con mucha mayor rapidez que uno que se
debe interpretar a cada paso durante una ejecución del código.
La mayoría de lenguajes de alto nivel permiten la programación
multipropósito, aunque muchos de ellos fueron diseñados para permitir
programación dedicada, como lo fue el Pascal con las matemáticas en su
comienzo. También se han implementado lenguajes educativos infantiles
como Logo mediante una serie de simples instrucciones. En la actualidad son
muy populares algunos lenguajes especialmente indicados para aplicaciones
web, como Perl, PHP, Ruby, Python o JavaScript.

Técnica[editar]

Libros sobre diversos lenguajes de programación.

Para escribir programas que proporcionen los mejores resultados, cabe tener
en cuenta una serie de detalles.
 Corrección. Un programa es correcto si hace lo que debe hacer tal y
como se estableció en las fases previas a su desarrollo. Para determinar
si un programa hace lo que debe, es muy importante especificar
claramente qué debe hacer el programa antes de desarrollarlo y, una vez
acabado, compararlo con lo que realmente hace.

 Claridad. Es muy importante que el programa sea lo más claro y legible


posible, para facilitar así su desarrollo y posterior mantenimiento. Al
elaborar un programa se debe intentar que su estructura sea sencilla y
coherente, así como cuidar el estilo en la edición; de esta forma se ve
facilitado el trabajo del programador, tanto en la fase de creación como en
las fases posteriores de corrección de errores, ampliaciones,
modificaciones, etc. Fases que pueden ser realizadas incluso por otro
programador, con lo cual la claridad es aún más necesaria para que otros
programadores puedan continuar el trabajo fácilmente. Algunos
programadores llegan incluso a utilizar Arte ASCII para delimitar
secciones de código. Otros, por diversión o para impedir un análisis
cómodo a otros programadores, recurren al uso de código ofuscado.

 Eficiencia. Se trata de que el programa, además de realizar aquello para


lo que fue creado (es decir, que sea correcto), lo haga gestionando de la
mejor forma posible los recursos que utiliza. Normalmente, al hablar de
eficiencia de un programa, se suele hacer referencia al tiempo que tarda
en realizar la tarea para la que ha sido creado y a la cantidad de memoria
que necesita, pero hay otros recursos que también pueden ser de
consideración al obtener la eficiencia de un programa, dependiendo de su
naturaleza (espacio en disco que utiliza, tráfico de red que genera, etc.).

 Portabilidad. Un programa es portable cuando tiene la capacidad de


poder ejecutarse en una plataforma, ya sea hardware o software,
diferente a aquella en la que se elaboró. La portabilidad es una
característica muy deseable para un programa, ya que permite, por
ejemplo, a un programa que se ha desarrollado para
sistemas GNU/Linuxejecutarse también en la familia de sistemas
operativos Windows. Esto permite que el programa pueda llegar a más
usuarios más fácilmente.
Paradigmas[editar]
Los programas se pueden clasificar por el paradigma del lenguaje que se use
para producirlos. Los principales paradigmas
son: imperativos, declarativos y orientación a objetos.
Los programas que usan un lenguaje imperativo especifican un algoritmo,
usan declaraciones, expresiones y sentencias.6 Una declaración asocia un
nombre de variable con un tipo de dato, por ejemplo: var x: integer; .
Una expresión contiene un valor, por ejemplo: 2 + 2 contiene el valor 4.
Finalmente, una sentencia debe asignar una expresión a una variable o usar
el valor de una variable para alterar el flujo de un programa, por
ejemplo: x := 2 + 2; if x == 4 then haz_algo(); . Una crítica común
en los lenguajes imperativos es el efecto de las sentencias de asignación
sobre una clase de variables llamadas "no locales".7
Los programas que usan un lenguaje declarativo especifican las propiedades
que la salida debe conocer y no especifican cualquier detalle de
implementación. Dos amplias categorías de lenguajes declarativos son
los lenguajes funcionales y los lenguajes lógicos. Los lenguajes funcionales
no permiten asignaciones de variables no locales, así, se hacen más fácil, por
ejemplo, programas como funciones matemáticas.7 El principio detrás de los
lenguajes lógicos es definir el problema que se quiere resolver (el objetivo) y
dejar los detalles de la solución al sistema.8 El objetivo es definido dando una
lista de sub-objetivos. Cada sub-objetivo también se define dando una lista de
sus sub-objetivos, etc. Si al tratar de buscar una solución, una ruta de sub-
objetivos falla, entonces tal sub-objetivo se descarta y sistemáticamente se
prueba otra ruta.
La forma en la cual se programa puede ser por medio de texto o de forma
visual. En la programación visual los elementos son manipulados
gráficamente en vez de especificarse por medio de texto.

Algoritmo
Ir a la navegaciónIr a la búsqueda

Los diagramas de flujo sirven para representar algoritmos de manera gráfica

En matemáticas, lógica, ciencias de la computación y disciplinas relacionadas,


un algoritmo (del latín, dixit algorithmus y este del griego arithmos, que significa «número»,
quizá también con influencia del nombre del matemático persa Al-Juarismi)1 es un conjunto
prescrito de instrucciones o reglas bien definidas, ordenadas y finitas que permiten llevar a
cabo una actividad mediante pasos sucesivos que no generen dudas a quien deba hacer
dicha actividad.2 Dados un estado inicial y una entrada, siguiendo los pasos sucesivos se llega
a un estado final y se obtiene una solución. Los algoritmos son el objeto de estudio de
la algoritmia.1
En la vida cotidiana, se emplean algoritmos frecuentemente para resolver problemas. Algunos
ejemplos son los manuales de usuario, que muestran algoritmos para usar un aparato, o las
instrucciones que recibe un trabajador de su patrón. Algunos ejemplos en matemáticason
el algoritmo de multiplicación, para calcular el producto, el algoritmo de la división para
calcular el cociente de dos números, el algoritmo de Euclides para obtener el máximo común
divisor de dos enteros positivos, o el método de Gauss para resolver un sistema de
ecuaciones lineales.
En términos de programación, un algoritmo es una secuencia de pasos lógicos que permiten
solucionar un problema. Los derechos de autor otorgan al propietario el derecho exclusivo
sobre el uso de la obra, con algunas excepciones. Cuando alguien crea una obra original fija
en un medio tangible, automáticamente se convierte en el propietario de los derechos de autor
de dicha obra.

Índice

 1Definición
 2Medios de expresión de un algoritmo
o 2.1Diagrama de flujo
o 2.2Pseudocódigo
o 2.3Sistemas formales
o 2.4Implementación
o 2.5Variables
o 2.6Estructuras secuenciales
 3Algoritmos como funciones
 4Análisis de algoritmos
 5Ejemplo de algoritmo
o 5.1Descripción de alto nivel
o 5.2Descripción formal
o 5.3Implementación
 6Véase también
o 6.1Tipos de algoritmos según su función
o 6.2Técnicas de diseño de algoritmos
o 6.3Temas relacionados
o 6.4Disciplinas relacionadas
 7Referencias
 8Bibliografía
 9Enlaces externos

Definición[editar]
En general, no existe ningún consenso definitivo en cuanto a la definición formal de algoritmo.
Muchos autores los señalan como listas de instrucciones para resolver un cálculo o
un problema abstracto, es decir, que un número finito de pasos convierten los datos de un
problema (entrada) en una solución (salida).123456 Sin embargo cabe notar que algunos
algoritmos no necesariamente tienen que terminar o resolver un problema en particular. Por
ejemplo, una versión modificada de la criba de Eratóstenes que nunca termine de calcular
números primos no deja de ser un algoritmo.7
A lo largo de la historia varios autores han tratado de definir formalmente a los algoritmos
utilizando modelos matemáticos. Esto fue realizado por Alonzo Church en 1936 con el
concepto de "calculabilidad efectiva" basada en su cálculo lambda y por Alan
Turing basándose en la máquina de Turing. Los dos enfoques son equivalentes, en el sentido
en que se pueden resolver exactamente los mismos problemas con ambos enfoques.89 Sin
embargo, estos modelos están sujetos a un tipo particular de datos como son números,
símbolos o gráficas mientras que, en general, los algoritmos funcionan sobre una vasta
cantidad de estructuras de datos.31 En general, la parte común en todas las definiciones se
puede resumir en las siguientes tres propiedades siempre y cuando no
consideremos algoritmos paralelos:7

 Tiempo secuencial. Un algoritmo funciona en tiempo discretizado –paso a paso–,


definiendo así una secuencia de estados computacionales por cada entrada válida
(la entrada son los datos que se le suministran al algoritmo antes de comenzar).
 Estado abstracto. Cada estado computacional puede ser descrito formalmente utilizando
una estructura de primer orden y cada algoritmo es independiente de su implementación
(los algoritmos son objetos abstractos) de manera que en un algoritmo las estructuras de
primer orden son invariantes bajo isomorfismo.
 Exploración acotada. La transición de un estado al siguiente queda completamente
determinada por una descripción fija y finita; es decir, entre cada estado y el siguiente
solamente se puede tomar en cuenta una cantidad fija y limitada de términos del estado
actual.
En resumen, un algoritmo es cualquier cosa que funcione paso a paso, donde cada paso se
pueda describir sin ambigüedad y sin hacer referencia a una computadora en particular, y
además tiene un límite fijo en cuanto a la cantidad de datos que se pueden leer/escribir en un
solo paso. Esta amplia definición abarca tanto a algoritmos prácticos como aquellos que solo
funcionan en teoría, por ejemplo el método de Newton y la eliminación de Gauss-
Jordan funcionan, al menos en principio, con números de precisión infinita; sin embargo no es
posible programar la precisión infinita en una computadora, y no por ello dejan de ser
algoritmos.10 En particular es posible considerar una cuarta propiedad que puede ser usada
para validar la tesis de Church-Turing de que toda función calculable se puede programar en
una máquina de Turing (o equivalentemente, en un lenguaje de programación suficientemente
general):10

 Aritmetizabilidad. Solamente operaciones innegablemente calculables están disponibles


en el paso inicial.

Medios de expresión de un algoritmo[editar]


Los algoritmos pueden ser expresados de muchas maneras, incluyendo al lenguaje
natural, pseudocódigo, diagramas de flujo y lenguajes de programación entre otros. Las
descripciones en lenguaje natural tienden a ser ambiguas y extensas. El usar pseudocódigo y
diagramas de flujo evita muchas ambigüedades del lenguaje natural. Dichas expresiones son
formas más estructuradas para representar algoritmos; no obstante, se mantienen
independientes de un lenguaje de programación específico.
La descripción de un algoritmo usualmente se hace en tres niveles:

1. Descripción de alto nivel. Se establece el problema, se selecciona un modelo


matemático y se explica el algoritmo de manera verbal, posiblemente con ilustraciones
y omitiendo detalles.
2. Descripción formal. Se usa pseudocódigo para describir la secuencia de pasos que
encuentran la solución.
3. Implementación. Se muestra el algoritmo expresado en un lenguaje de programación
específico o algún objeto capaz de llevar a cabo instrucciones.
También es posible incluir un teorema que demuestre que el algoritmo es correcto, un análisis
de complejidad o ambos.
Diagrama de flujo[editar]

Diagrama de flujo que expresa un algoritmo para calcular la raíz cuadrada de un número

Artículo principal: Diagrama de flujo

Los diagramas de flujo son descripciones gráficas de algoritmos; usan símbolos conectados
con flechas para indicar la secuencia de instrucciones y están regidos por ISO.
Los diagramas de flujo son usados para representar algoritmos pequeños, ya que abarcan
mucho espacio y su construcción es laboriosa. Por su facilidad de lectura son usados como
introducción a los algoritmos, descripción de un lenguaje y descripción de procesos a
personas ajenas a la computación.
Pseudocódigo[editar]
Artículo principal: Pseudocódigo

El pseudocódigo (falso lenguaje, el prefijo pseudo significa falso) es una descripción de alto
nivel de un algoritmo que emplea una mezcla de lenguaje natural con algunas convenciones
sintácticas propias de lenguajes de programación, como asignaciones, ciclos y condicionales,
aunque no está regido por ningún estándar. Es utilizado para describir algoritmos en libros y
publicaciones científicas, y como producto intermedio durante el desarrollo de un algoritmo,
como los diagramas de flujo, aunque presentan una ventaja importante sobre estos, y es que
los algoritmos descritos en pseudocódigo requieren menos espacio para representar
instrucciones complejas.
El pseudocódigo está pensado para facilitar a las personas el entendimiento de un algoritmo, y
por lo tanto puede omitir detalles irrelevantes que son necesarios en una implementación.
Programadores diferentes suelen utilizar convenciones distintas, que pueden estar basadas en
la sintaxis de lenguajes de programación concretos. Sin embargo, el pseudocódigo, en
general, es comprensible sin necesidad de conocer o utilizar un entorno de programación
específico, y es a la vez suficientemente estructurado para que su implementación se pueda
hacer directamente a partir de él.
Así el pseudocódigo cumple con las funciones antes mencionadas para representar algo
abstracto los protocolos son los lenguajes para la programación. Busque fuentes más precisas
para tener mayor comprensión del tema.
Sistemas formales[editar]
La teoría de autómatas y la teoría de funciones recursivas proveen modelos matemáticos que
formalizan el concepto de algoritmo. Los modelos más comunes son la máquina de
Turing, máquina de registro y funciones μ-recursivas. Estos modelos son tan precisos como
un lenguaje máquina, careciendo de expresiones coloquiales o ambigüedad, sin embargo se
mantienen independientes de cualquier computadora y de cualquier implementación.
Implementación[editar]
Muchos algoritmos son ideados para implementarse en un programa. Sin embargo, los
algoritmos pueden ser implementados en otros medios, como una red neuronal, un circuito
eléctrico o un aparato mecánico y eléctrico. Algunos algoritmos inclusive se diseñan
especialmente para implementarse usando lápiz y papel. El algoritmo de
multiplicacióntradicional, el algoritmo de Euclides, la criba de Eratóstenes y muchas formas de
resolver la raíz cuadrada son solo algunos ejemplos.
Variables[editar]
Son elementos que toman valores específicos de un tipo de datos concreto. La declaración de
una variable puede realizarse comenzando con var. Principalmente, existen dos maneras de
otorgar valores iniciales a variables:

1. Mediante una sentencia de asignación.


2. Mediante un procedimiento de entrada de datos (por ejemplo: 'read').

Ejemplo:

...
i:=1;
read(n);
while i < n do begin
(* cuerpo del bucle *)
i := i + 1
end;
...

Estructuras secuenciales[editar]
La estructura secuencial es aquella en la que una acción sigue a otra en secuencia. Las
operaciones se suceden de tal modo que la salida de una es la entrada de la siguiente y así
sucesivamente hasta el fin del proceso. La asignación de esto consiste, en el paso de valores
o resultados a una zona de la memoria. Dicha zona será reconocida con el nombre de la
variable que recibe el valor. La asignación se puede clasificar de la siguiente forma:

1. Simples: Consiste en pasar un valor constante a una variable (a ← 15)


2. Contador: Consiste en usarla como un verificador del número de veces que se realiza
un proceso (a ← a + 1)
3. Acumulador: Consiste en usarla como un sumador en un proceso (a ← a + b)
4. De trabajo: Donde puede recibir el resultado de una operación matemática que
involucre muchas variables (a ← c + b*1/2).
Un ejemplo de estructura secuencial, como obtener el área de un triángulo:

Inicio
...
float b, h, a;
printf("Diga la base");
scanf("%f", &b);
printf("Diga la altura");
scanf("%f", &h);
a = (b*h)/2;
printf("El área del triángulo es %f", a)
...
Fin

Algoritmos como funciones[editar]


Artículo principal: Teoría de la computabilidad

Esquemática de un algoritmo solucionando un problema de ciclo hamiltoniano

Un algoritmo se puede concebir como una función que transforma los datos de
un problema (entrada) en los datos de una solución (salida). Mas aún, los datos se pueden
representar a su vez como secuencias de bits, y en general, de símbolos cualesquiera.1911
Como cada secuencia de bits representa a un número natural(véase Sistema binario),
entonces los algoritmos son en esencia funciones de los números naturales en los números

naturales que sí se pueden calcular. Es decir que todo algoritmo calcula una función
donde cada número natural es la codificación de un problema o de una solución.
En ocasiones los algoritmos son susceptibles de nunca terminar, por ejemplo, cuando entran a
un bucle infinito. Cuando esto ocurre, el algoritmo nunca devuelve ningún valor de salida, y
podemos decir que la función queda indefinida para ese valor de entrada. Por esta razón se
considera que los algoritmos son funciones parciales, es decir, no necesariamente definidas
en todo su dominio de definición.
Cuando una función puede ser calculada por medios algorítmicos, sin importar la cantidad de
memoria que ocupe o el tiempo que se tarde, se dice que dicha función es computable. No
todas las funciones entre secuencias datos son computables. El problema de la parada es un
ejemplo.

Análisis de algoritmos[editar]
Artículo principal: Análisis de algoritmos

Como medida de la eficiencia de un algoritmo, se suelen estudiar los recursos (memoria y


tiempo) que consume el algoritmo. El análisis de algoritmos se ha desarrollado para obtener
valores que de alguna forma indiquen (o especifiquen) la evolución del gasto de tiempo y
memoria en función del tamaño de los valores de entrada.
El análisis y estudio de los algoritmos es una disciplina de las ciencias de la computación y, en
la mayoría de los casos, su estudio es completamente abstracto sin usar ningún tipo
de lenguaje de programación ni cualquier otra implementación; por eso, en ese sentido,
comparte las características de las disciplinas matemáticas. Así, el análisis de los algoritmos
se centra en los principios básicos del algoritmo, no en los de la implementación particular.
Una forma de plasmar (o algunas veces "codificar") un algoritmo es escribirlo
en pseudocódigo o utilizar un lenguaje muy simple tal como Lexico, cuyos códigos pueden
estar en el idioma del programador.
Algunos escritores restringen la definición de algoritmo a procedimientos que deben acabar en
algún momento, mientras que otros consideran procedimientos que podrían ejecutarse
eternamente sin pararse, suponiendo el caso en el que existiera algún dispositivo físico que
fuera capaz de funcionar eternamente. En este último caso, la finalización con éxito del
algoritmo no se podría definir como la terminación de este con una salida satisfactoria, sino
que el éxito estaría definido en función de las secuencias de salidas dadas durante un periodo
de vida de la ejecución del algoritmo. Por ejemplo, un algoritmo que verifica que hay más
ceros que unos en una secuencia binaria infinita debe ejecutarse siempre para que pueda
devolver un valor útil. Si se implementa correctamente, el valor devuelto por el algoritmo será
válido, hasta que evalúe el siguiente dígito binario. De esta forma, mientras evalúa la siguiente
secuencia podrán leerse dos tipos de señales: una señal positiva (en el caso de que el número
de ceros sea mayor que el de unos) y una negativa en caso contrario. Finalmente, la salida de
este algoritmo se define como la devolución de valores exclusivamente positivos si hay más
ceros que unos en la secuencia y, en cualquier otro caso, devolverá una mezcla de señales
positivas y negativas.

Ejemplo de algoritmo[editar]
El problema consiste en encontrar el máximo de un conjunto de números. Para un ejemplo
más complejo véase Algoritmo de Euclides.
Descripción de alto nivel[editar]

Dado un conjunto finito de números, se tiene el problema de encontrar el número más


grande. Sin pérdida de generalidad se puede asumir que dicho conjunto no es vacío y que sus

elementos están numerados como .

Es decir, dado un conjunto se pide encontrar tal que para todo

elemento que pertenece al conjunto .


Para encontrar el elemento máximo, se asume que el primer elemento ( ) es el máximo;
luego, se recorre el conjunto y se compara cada valor con el valor del máximo número
encontrado hasta ese momento. En el caso que un elemento sea mayor que el máximo, se
asigna su valor al máximo. Cuando se termina de recorrer la lista, el máximo número que se
ha encontrado es el máximo de todo el conjunto.
Descripción formal[editar]
El algoritmo puede ser escrito de una manera más formal en el siguiente pseudocódigo:

Algoritmo Encontrar el máximo de un conjunto

función max( )

// es un conjunto no vacío de números//

← // es el número de elementos de //

para ← hasta hacer

si entonces

devolver

Sobre la notación:

 "←" representa una asignación: ← significa que la variable toma el valor

de ;
 "devolver" termina el algoritmo y devuelve el valor a su derecha (en este caso, el máximo

de ).
Implementación[editar]
En lenguaje C++:

int max(int c[], int n)


{
int i, m = c[0];
for (i = 1; i < n; i++)
if (c[i] > m) m = c[i];
return m;
}

1.1 Definición de algoritmo

Un algoritmo es una secuencia de pasos lógicos necesarios para llevar a cabo una tarea especifica, como la

solución de un problema. 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.

Por ejemplo en una analogía con la vida diaria, una receta de un plato de cocina se puede expresar en

español, ingles o francés, pero cualquiera que sea el lenguaje, los pasos para la elaboración del plato se

realizaran sin importar el cocinero.

Los pasos a seguir en la solución de una ecuación de segundo grado.

Los pasos matemáticos para la solución de un número factorial.

Las instrucciones para la liquidación de una nomina.

Las acciones que se deben seguir para la obtención de una estadística.

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.

Características de los algoritmos

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 resultado

cada vez.

Un algoritmo debe ser finito. Si se sigue un algoritmo, se debe terminar en algún momento; o sea debe de

tener un número finito de pasos.

La definición de un algoritmo debe describir tres partes: Entrada, Proceso y Salida.

En el algoritmo citado anteriormente se tendrá:

Entrada ingredientes y utensilios empleados

Proceso elaboración de la receta de cocina

Salida terminación del plato (por ejemplo, cordero)


Un algoritmo exige que se tengan varias propiedades importantes:

Los pasos de un algoritmo deben ser simples y exentos de ambigüedades (diferentes significados), deben

seguir un orden cuidadosamente prescrito, deben ser efectivos y deben de resolver el problema en un

número finito de pasos.

El siguiente ejemplo muestra un algoritmo para cambiar un foco quemado.

Cambiar un foco quemado podría resumirse en dos pasos:

Quitar el foco quemado

Colocar un foco nuevo

Pero, si tuviera que entrenar un robot domestico para que efectúe esta tarea, tendrá que ser mas especifico

y claro en los pasos a seguir, dar mas detalles (suponga que el foco se encuentra en el techo de una

habitación):

Situar escalera bajo el foco quemado.

Elegir un foco de reemplazo (de la misma potencia que el anterior).

Subir por la escalera hasta alcanzar el foco.

Girar el foco contra las manecillas del reloj hasta que esté suelto.

Ubicar el foco nuevo en el mismo lugar que el anterior.

Enroscar en el sentido de las manecillas del reloj hasta que quede apretado.

Bajar de la escalera.

1.2 Lenguajes de programación (Lenguaje máquina, ensamblador y de alto nivel)

Al igual que los idiomas sirven de vehículo de comunicación entre seres humanos, existen lenguajes que

realizan la comunicación entre ellos y las computadoras. Estos lenguajes permiten expresar las instrucciones

que el programador desea que la computadora ejecute.

Los principales tipos de lenguajes utilizados en la actualidad son tres:

Lenguaje maquina

Lenguaje de bajo nivel (ensamblador)

Lenguajes de alto nivel

Lenguajes máquina

Se llama lenguaje máquina a las instrucciones que se dan directamente a la computadora, utilizando una

serie de dígitos binarios o bits, representados por los números 0 y 1 que especifican una operación. Aunque

este lenguaje es el que entiende la computadora, es muy difícil de manejar en la comunicación humana. Las
instrucciones en lenguaje maquina dependen del hardware de la computadora y, por lo tanto, diferirán de

una computadora a otra.

Lenguajes de bajo nivel (ensamblador)

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 ensamblador (assembler

lenguaje). Las instrucciones en lenguaje ensamblador son conocidas como mnemotécnicos.

Por ejemplo, mnemotécnicos típicos de operaciones aritméticas son:

en ingles, ADD, SUB, DIV, etc.

en español, SUM,RES,DIV, etc.

Una instrucción típica de suma seria:

ADD M, N, P

Esta instrucción podría significar "sumar el número contenido en la posición de memoria M al 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 mnemoté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 maquina se conoce como programa objeto, ya directamente entendible por la computadora.

El traductor de programas fuente a objeto es un programa llamado ensamblador (assembler), existente en

casi todas las computadoras.

NOTA: No se debe confundir el programa ensamblador, encargado de efectuar la traducción del programa

fuente escrito a lenguaje maquina, con el lenguaje ensamblador (assembly language), lenguaje de

programación con una estructura y gramática definidas.

Los lenguajes ensambladores presentan la ventaja frente a los lenguajes maquina de su mayor facilidad de

codificación y, en general, su velocidad de cálculo.

Los inconvenientes más notables de los lenguajes ensambladores son:

Dependencia total de la maquina lo que impide la transportabilidad de los programas (posibilidad de ejecutar

un programa en diferentes maquinas).


La formación de los programadores 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.

Hoy día los lenguajes ensambladores tienen sus aplicaciones muy reducidas en la programación de

aplicaciones y se centran en aplicaciones de tiempo real, control de procesos y de dispositivos electrónicos,

etc.

Lenguajes de alto nivel

Los lenguajes de alto nivel son los mas 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 un 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 lenguajes 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. Nombres de las

instrucciones tales como READ, WRITE, PRINT, OPEN, etc. 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 fuente para

conseguir el programa definitivo.

No se aprovechan los recursos internos de la máquina que se explotan mucho mejor en lenguajes máquina y

ensambladores.

Aumento de la ocupación de memoria.

El tiempo de ejecución de los programas es mucho mayor.

Al igual que pasa con los lenguajes ensambladores, los programas fuente tienen que ser traducidos por

programas traductores, llamados compiladores e interpretes.


Los lenguajes de programación de alto nivel existentes en la actualidad son muy numerosos, aunque la

práctica demuestra que su uso mayoritario se reduce a BASIC, COBOL, PASCAL, C, C++,... y en el campo de

la primera enseñanza a LOGO, PILOT...

1.3 Traductores de lenguaje

Los traductores de lenguajes son programas que traducen a su vez los programas fuente escritos en

lenguajes de alto nivel a código máquina.

Los traductores se dividen en:

Compiladores

Interpretes

Interpretes

Un interprete es un traductor que toma un programa fuente, lo traduce y a continuación lo ejecuta (dicho

programa por medio de la computadora desarrolla una tarea especifica).

Un lenguaje que soporte un traductor de tipo intérprete se denomina lenguaje interpretado. BASIC es el

modelo por excelencia interpretado.

Los programas fuente en BASIC se escriben con ayuda de un programa denominado editor que suele venir

incorporado al programa intérprete.

Compiladores

Un compilador es un programa que traduce los programas fuente escritos en lenguajes de alto nivel a

lenguaje máquina.

Los programas escritos en lenguajes de alto nivel (en el editor del lenguaje) se llaman programas fuente y el

programa traducido programa objeto o código objeto. El compilador traduce (sentencia a sentencia) el

programa fuente.

Lenguajes compiladores típicos son: PASCAL, COBOL, C..

Fases de la compilación

La compilación es el proceso de la traducción de programas fuente a programas objeto.


El programa objeto obtenido de la compilación no ha sido traducido normalmente a código máquina sino a

ensamblador. 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:

Por ejemplo, el proceso de ejecución de un Programa en C++ tiene 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 (un disco).

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 montador obtiene el programa ejecutable.

Se ejecuta el programa y si no existen errores, se tendrá la salida del mismo.


1.4 Definición de programa

Un programa de computadora es un conjunto de instrucciones (ordenes dadas a la máquina) que producirán

la ejecución de una determinada tarea. En esencia, un programa es un medio para conseguir un fin. El fin

será normalmente definido como la información necesaria para solucionar un problema.

El proceso de programación es, por consiguiente, un proceso de solución de problemas (como ya se vio

anteriormente) y el desarrollo de un programa requiere las siguientes fases:

1. Definición y análisis del problema.

2. Diseño de algoritmos.

- diagrama de flujo;

- pseudocódigo.

3. Codificación del programa.

4. Depuración y verificación del programa.

5. Documentación.

6. Mantenimiento.

En este curso el objetivo fundamental son las fases 1 y 2.

Vous aimerez peut-être aussi