¡Consigue un libro de programación gratis todos los días! PACKT los regala hasta el 26 de diciembre


En esta época del año, los regalos suelen aparecer por todas partes y es imposible pasarlos por alto, ¡sobre todo cuando son tan fenomenales! En esta oportunidad les mostramos esta gran oferta para todos nuestros lectores. Por un período limitado y ahora hasta el 26 de diciembre, PACKT Publishing está ofreciendo un eBook gratis por día en su sitio web bajo la iniciativa del Aprendizaje Gratuito. Se trata de libros de temática bastante actualizada. ¡El precio regular de estos libros oscila entre los 30 y 40 dólares! Pero ahora los podrás conseguir sin pagar un solo centavo, sin trucos, sin restricciones, puedes visitar el sitio todos los días y obtener tantos libros gratuitos como puedas mientras la oferta se mantenga activa, un libro por día. Conseguirás dichos libros para siempre y todos los contenidos son libres de DRM. ¡Puedes invitar a tus amigos y colegas para que también aprovechen esta oferta de aprendizaje gratuito!

PACKT Publishing es la principal editorial proveedora de libros de programación e informática en el Reino Unido y  una de las editoriales de libros sobre tecnología más importantes y de más rápido crecimiento a nivel mundial.

¡Vamos! Dale clic al siguiente enlace para acceder a esta espectacular oferta:

Estructuras de Control

Normalmente, el flujo de ejecución de un programa es secuencial, porque se ejecuta una sentencia tras otra. Las estructuras de control permiten modificar el flujo secuencial, alterando el orden de las sentencias bajo determinadas circunstancias. En los lenguajes estructurados, existen dos estructuras de control básicas: la selección y la repetición o iteración. Según el teorema de Dijkstra, estas dos estructuras, junto con la secuencia, son suficientes para escribir cualquier programa.


La estructura de selección

La estructura de selección permite determinar si una sentencia (simple o compuesta) será ejecutada o no a partir de la evaluación de una determinada condición o expresión booleana. En la mayoría de los lenguajes actuales, la selección se realiza mediante el uso de la estructura if (si). La forma más básica de esta estructura es la siguiente (en pseudocódigo):

Si (expresión booleana)
    Entonces ejecutar sentencia A
    Si no ejecutar sentencia B

La segunda parte (la del si no) es optativa y permite ejecutar una acción alternativa para cuando el resultado de la expresión lógica es falso.

En C#, la estructura de selección se escribe mediante la palabra if seguida de la expresión entre paréntesis y debajo la sentencia por ejecutar en caso de que la expresión sea verdadera. En forma opcional, puede haber un else (si no) para especificar una acción en caso de que la expresión lógica sea falsa.

//  C#
if( saldo > montoExtraccion )
RealizarExtraccion(montoExtraccion);
else
MostrarMensaje(“No tiene suficiente saldo”);

En Visual Basic .Net, la estructura de selección es muy similar, aunque con algunas diferencias: la expresión no necesita ir entre paréntesis y debe ser seguida por la palabra reservada THEN (entonces). Además, VB.Net provee la posibilidad de escribir la acción en la misma línea del if o en la línea siguiente. En caso que se escriba debajo, la estructura debe ser cerrada usando end if.

' VB.NET
If saldo > montoExtraccion Then RealizarExtraccion(montoExtraccion)
Else
MostrarMensaje(“No tiene suficiente saldo”)
End If

' Tambien podemos hacer esto:
If b > 0 Then c = a / b else c = 0

CLASIFICACIONES DE LOS TIPOS DE DATOS

Los tipos de datos pueden clasificarse en tres grupos: los ordinales, los no ordinales y los compuestos. Los tipos de datos ordinales son aquellos cuyos valores pueden ser ordenados (se establece una relación de orden entre cualquier par de valores), como por ejemplo, los números enteros o los caracteres de la tabla ASCII. Los valores de los tipos no ordinales no pueden ordenarse, como es el caso de los números decimales de gran precisión o los punteros a memoria. Los tipos de datos compuestos incluyen las cadenas de caracteres y las estructuras de datos.

En muchos lenguajes, existe otra estructura de selección alternativa, denominada comúnmente case o select case, que permite especificar varias alternativas por casos, según el valor de una variable o de una expresión. Si bien el select case puede escribirse perfectamente como una secuencia de estructuras if, en pseudocódigo, podemos escribir la selección múltiple de esta manera:

SegunEsElValorDe  expresión hacer:

C1: sentencia 1
C2: sentencia 2


Sino: sentencia n

El funcionamiento de esta estructura es así: se evalúa la expresión, y su valor se compara con la lista de constantes C1, C2, etcétera. Si el valor de la expresión está comprendido en la lista de constantes, se ejecuta la sentencia correspondiente (por ejemplo, si el valor de la expresión es C2, se ejecuta la sentencia 2). Si el valor de la expresión no está en la lista de constantes, y hay un sino, se ejecuta la sentencia asociada a éste, caso contrario, la ejecución continúa en la línea siguiente. En C#, la estructura de selección múltiple cumple la misma función, pero se denomina switch, y su sintaxis es la siguiente:

//  C#
switch(variable)
{
case 10:
HacerAlgo(); break;
case 20:
HacerOtraCosa(); break;
default:
HacerAlgoDistinto(); break;
}

En Visual Basic .Net, la misma estructura selectiva múltiple se denomina Select Case y se escribe así:

‘ VB.Net
SELECT CASE variable
CASE 10: HacerAlgo()
CASE 20: HacerOtraCosa()
CASE ELSE: HacerAlgoDistinto();
END SELECT

Las estructuras de repetición

Muy a menudo, necesitamos repetir una sentencia más de una vez para completar el objetivo del algoritmo. Las estructuras de repetición de los lenguajes estructurados permiten ejecutar una sentencia una cierta cantidad de veces o mientras se cumpla una determinada condición. Por ejemplo, si volvemos a mirar el algoritmo del Capítulo 1, podremos notar que repetimos las sentencias que hacen las restas entre las dos variables mientras que el valor de la variable A sea mayor que cero.

Como decíamos en el párrafo anterior, la mayoría de los lenguajes estructurados proveen dos tipos de estructuras de repetición: una para repetir una sentencia mientras se cumpla una condición (es decir, mientras el resultado de evaluar una expresión lógica sea verdadero), y otra para repetir una sentencia una cantidad determinada de veces. La sentencia que se ejecuta repetidas veces se denomina bucle.

La estructura de repetición condicional se denomina comúnmente while (mientras). En el lenguaje C#, simplemente se la escribe seguida de la expresión lógica entre paréntesis y debajo la sentencia a ejecutar:

// C#
While( a > 0 )
a = a - b;

En Visual Basic .Net, es muy similar, con la diferencia que se debe marcar el final de la estructura con las palabras clave End While, y no es necesario escribir la expresión booleana entre paréntesis:

‘ VB.Net
While a > 0
a = a - b
End While

En algunos lenguajes, existe una variante de esta estructura de repetición, consistente en evaluar la expresión lógica al final. La diferencia radica en que, al evaluar la condición al final, la sentencia por repetir se ejecutará por lo menos una vez, mientras que, si la evaluación se hace al comienzo, la sentencia podría no ejecutarse nunca.

Al momento de usar una estructura while, se debe prestar especial atención a la expresión lógica que determina el fin del bucle (también conocida como condición de corte), ya que escribirla mal, podría hacer que el bucle nunca finalizara (bucle infinito). Por ejemplo, si la condición de corte es que el valor de una variable llegue a cero, pero olvidamos modificarlo dentro del bucle, el valor permanecerá constante, y el ciclo nunca finalizará.

La otra estructura de control repetitiva con la que disponemos en la mayoría de los lenguajes estructurados es la que se conoce como For. Esta estructura permite especificar que se quiere repetir la sentencia una cantidad finita de veces, usando una variable como contador. A cada iteración, la variable contador se incrementa automáticamente (es decir, no necesitamos preocuparnos por actualizarla como sucede con while). En algunos lenguajes, como Pascal, no se permite modificar la variable contadora dentro del bucle para facilitar la traducción y validación del código durante la fase de compilación.

En C#, la estructura for es un poco difícil de comprender al comienzo, pero una vez que nos familiarizamos con ella, vemos que tiene una gran capacidad de expresividad y de flexibilidad para escribir repeticiones complejas. Al escribir un for en C#, debemos especificar el valor con el que comienza la variable contadora, la condición que determina que el contador ha llegado al valor final y la expresión que incrementa el contador. Esto último parece contradecir lo que mencionábamos antes acerca de que no es necesario preocuparnos por modificar el contador. Y es cierto. En ese aspecto, la estructura for de C# es parecida a la estructura while. Veámosla en un ejemplo:

// C#
for(i=0;i<10;i++)
HacerAlgo();

Como se puede ver en el ejemplo, la estructura consta de tres partes fundamentales separadas por punto y coma. La primera especifica el valor del contador con el que comienza la iteración. La segunda, la condición para finalizar. Y la última especifica la expresión que modifica el valor del contador en cada iteración. En este ejemplo, la sentencia HacerAlgo se ejecutará diez veces. El for de C# soporta algunas variantes que lo hacen más complejo y potente, pero su explicación excede el alcance de este libro.

En Visual Basic .Net, la estructura for es mucho más sencilla e intuitiva, pero también menos potente. Además, se diferencia de la versión en C# en que debe ser cerrado con la palabra reservada Next (siguiente). Continuando con el ejemplo anterior, en Visual Basic podemos escribir:

‘VB.Net
For i=1 to 10
HacerAlgo()
Next

En este ejemplo, la sentencia HacerAlgo se repetirá diez veces, y, en cada iteración, el valor de la variable i se incrementará automáticamente en 1.

CONCLUSIONES

En el momento de su aparición, la programación estructurada significó un importante avance en las formas de construir software, sobre todo por el impacto que causó en los costos de mantenimiento. La facilidad de mantenimiento se debe a que, con la programación estructurada, conseguimos programas más fáciles de entender y de depurar (por la ausencia de instrucciones GOTO), además, los bloques de código son más claros.

Operadores y Expresiones

Anteriormente, vimos que un tipo de datos define los posibles valores que puede tomar una variable con el conjunto de operaciones o de acciones que se realizan sobre esos valores. Podemos definir un operador como el símbolo utilizado para indicar una operación sobre elementos llamados operandos. Por ejemplo, para el tipo de dato número entero, está definida la operación de suma, que puede representarse mediante el signo +. Así, si escribimos 2 + 3, indicamos que queremos aplicar la operación de suma a los valores (operandos) 2 y 3. Del mismo modo, si escribimos saldo + 100, mostramos que queremos sumar 100 al valor almacenado en la variable identificada como saldo. Cuando unimos dos o más constantes o variables mediante un operador, definimos una expresión.

Las expresiones pueden ser simples o compuestas. Una expresión simple es aquella sólo formada por un operador con los operandos necesarios. Una expresión compuesta está formada por dos o más operaciones simples. En el caso de las expresiones compuestas, es necesario especificar con exactitud el orden en que se resuelven las expresiones simples que las componen. En el caso de operadores matemáticos, generalmente, el orden en que se resuelven es el tradicional (es decir, primero el producto y la división, y luego la suma y la resta). Si al momento de escribir la expresión no tenemos del todo claro cuál es el orden de prioridad de los operadores, podemos utilizar paréntesis para alterar o hacer implícito el orden de resolución.

TIPOS DE OPERADORES

Para no entrar en detalles muy matemáticos, podemos identificar tres grandes tipos de operadores: los lógicos, los aritméticos y los relacionales.

Operadores lógicos

Los operadores lógicos relacionan uno o más valores de verdad (es decir, valores que pueden ser verdadero o falso), y el resultado es otro valor de verdad en función de los operandos. Dentro de los lenguajes de programación, los operadores lógicos se utilizan para evaluar condiciones y modificar así el flujo de control de un programa. Los operadores lógicos más conocidos son no (NOT), o (OR), y (AND).

En la Tabla 1, pueden apreciarse los valores devueltos por los operadores lógicos más conocidos (usamos los nombres en inglés para ir familiarizándonos con los lenguajes de programación). En la jerga de la programación llamamos operadores booleanos a los operadores lógicos, por su relación con el álgebra de Boole.


Tabla de verdad de los cuatro operadores lógicos más comunes.

Operadores aritméticos

Como hemos visto, los operadores aritméticos se corresponden con las operaciones matemáticas tradicionales: la adición, la sustracción, la potenciación.

Operadores relacionales

Los operadores relacionales, tal como su nombre lo indica, establecen una relación entre sus operandos. Un ejemplo clásico de operador relacional es el operador mayor que, notado con el símbolo >. En general, los operadores relacionales devuelven un valor de verdad, por lo que son usados en expresiones lógicas (por ejemplo, el valor de verdad de la expresión 7 > 9 es falso).


George Boole fue el precursor en el álgebra de Boole, de donde se tomó el concepto de Operador lógico o booleano.

Tipos de operadores

Según la cantidad de operadores con los que trabaja un operador puede clasificarse en unario o binario. Un operador unario se aplica sólo a un operador. El ejemplo más común es el signo -, utilizado para indicar números negativos (por ejemplo, -15). Los operadores binarios poseen dos operandos, como por ejemplo, el signo +. El caso de los operadores binarios puede extenderse y generalizarse para una cantidad cualquiera de operadores, en cuyo caso se denominan operador n-arios, donde n es la cantidad de operandos. Como ejemplo, podemos imaginar un operador 4-ario llamado M que devuelve el máximo entre cuatro números. Una operación con este operador podría escribirse como M 7 2 0 9.

Según el lugar donde están los operadores con respecto a los operandos en una operación, un operador puede clasificarse en infijo, prefijo o sufijo. Un operador prefijo se coloca delante de los operadores. El ejemplo que veíamos antes para el signo -, usado como operador unario, es un caso de operador prefijo. Si el operador se coloca entre los operandos, entonces de denomina infijo (es el tipo más común entre los operadores matemáticos). Por ejemplo, el signo de adición (+) es un operador infijo, ya que se lo escribe entre los operandos (2 + 4). Por último, los operadores sufijos son aquellos que se colocan detrás de los operandos. Un ejemplo no tan conocido es el operador ++ del lenguaje C#. Este operador devuelve el valor del operador y luego le suma uno (podemos escribir por ejemplo: a++).

EL LENGUAJE PASCAL

El lenguaje Pascal fue creado por Niklaus Wirth con el objetivo de lograr un lenguaje fácil de aprender, para usar en sus clases de programación, y lo bautizó en honor a Blaise Pascal, inventor de la primera calculadora mecánica. Pascal es un lenguaje estructurado y fuertemente tipado, además de contar con características que hacen posible un buen grado de encapsulamiento. Actualmente, muchas universidades lo siguen utilizando para introducir los conceptos de programación estructurada.

Tipos de datos y Sentencias

TIPOS DE DATOS

Un tipo de dato es el conjunto de valores que toma una variable, junto con las operaciones que pueden realizarse sobre esos valores. En muchos lenguajes, es necesario asociar una variable con un tipo de dato, de manera que el compilador o intérprete pueda validar que las operaciones que hemos escrito realmente corresponden al tipo de dato que manejamos. Un ejemplo de tipo de datos es el número entero. El conjunto de valores que puede tomar es el de los números enteros (0, 1, 2, 1, etcétera), y las operaciones que podemos realizar sobre ellos son la suma, la resta, la multiplicación y la división de enteros.


Los lenguajes que exigen que definamos el tipo de dato de las variables antes de usarlas se denominan fuertemente tipados. Desde el punto de vista de la detección de errores, estos lenguajes presentan una gran ventaja frente a los no tipados ya que permiten detectar, durante la compilación (en el caso de lenguajes compilados), algunos errores muy habituales, como intentar asignar a una variable un dato que no le corresponde. Por ejemplo, si definimos la variable numerador como de tipo entero, pero más adelante le asignamos el texto Hola Mundo, el compilador de un lenguaje fuertemente tipado detectará el problema y nos lo informará antes de compilar. Si el lenguaje no es fuertemente tipado, esto no se detectará y podría ocurrir que, luego, cuando queramos hacer una multiplicación entre la variable numerador y el valor 5, la operación resulte en un error. Al no ser detectado por el compilador, este error surgirá durante la ejecución del programa.

En la plataforma .Net, C# es un lenguaje fuertemente tipado, mientras que en VB.Net existe la posibilidad de indicarle al compilador que exija o no la declaración de los tipos de datos de las variables.

SENTENCIAS

Las sentencias describen acciones que serán ejecutadas por la computadora como parte del funcionamiento del programa. Éstas se diferencian de las instrucciones en que una sentencia puede ser traducida por el compilador o por el intérprete en cero o más instrucciones de máquina. Es decir, una sentencia posee mayor abstracción y expresividad que una instrucción.

Las sentencias pueden clasificarse en ejecutables y no ejecutables. Las ejecutables son las que se traducen en instrucciones de la máquina y se utilizan para realizar acciones concretas (como sumar dos números o imprimir un texto en la pantalla del monitor). Por el contrario, las sentencias no ejecutables no se traducen a instrucciones, y su única funcionalidad reside en aumentar la legibilidad del código fuente, ya que se pueden usar como comentarios para explicar detalles de la implementación.

Al mismo tiempo, las sentencias pueden ser clasificadas en simples y compuestas. Una sentencia simple es una sentencia en sí misma y no contiene a ninguna otra. Por ejemplo, la sentencia de asignación utilizada para asignar un valor a una variable es una sentencia simple, ya que comprende únicamente la acción de asignar el valor a la variable. Por otro lado, las sentencias compuestas están formadas por dos o más sentencias que se ejecutan de acuerdo con alguna estructura de control, tal como veremos más adelante.

LOS COMENTARIOS EN C# Y EN VB.NET

Los comentarios son un caso especial de sentencia no ejecutable que permiten escribir un texto libre con el objetivo de aumentar la legibilidad del código. En cada lenguaje, la forma de escribir comentarios varía, como así también los tipos de comentarios que se pueden escribir (de una sola línea o de muchas líneas).

En C#, los comentarios de una sola línea se escriben comenzando con los caracteres // (por ejemplo: //esto es un comentario), mientras que los comentarios de muchas líneas se escriben comenzando con los caracteres /* y finalizando con */. Entre ambos caracteres, puede haber texto y saltos de línea.

En VB.Net, los comentarios de una sola línea se escriben comenzando con la comilla simple (‘), y no existen los comentarios múltiple línea (aunque se los puede simular escribiendo líneas consecutivas de comentarios, todas comenzadas con comilla simple).

Conceptos de Programación Estructurada

En las siguientes secciones, veremos los conceptos básicos de la programación estructurada, junto con su implementación utilizando lenguajes de la plataforma .NET.

IDENTIFICADORES Y PALABRAS CLAVE

Como vimos antes, un programa está formado por instrucciones. En los lenguajes de programación, sobre todo en los de nivel medio y alto, las instrucciones están formadas por palabras que pueden pertenecer o no a algún idioma humano. Como también vimos, los lenguajes de programación son más estrictos que los lenguajes naturales y no se permite el uso de cualquier palabra ni tampoco cualquier combinación de ellas. Toda palabra que se pueda usar en un programa, ya sea parte del lenguaje de programación o definida por nosotros, recibe el nombre de identificador. En todos los lenguajes de programación, los identificadores deben respetar ciertas reglas para que sea más sencillo el trabajo del compilador o del intérprete. La forma más común de representar un identificador es por una secuencia de uno o más caracteres consecutivos (no se permiten los espacios ni los saltos de línea) seguidos de caracteres o de números. Generalmente, no se permite que los identificadores comiencen con números.

C# Y VB.NET

Los dos lenguajes más importantes en .Net (y los únicos provistos por Microsoft desde la discontinuidad de J#) son C# y Visual Basic .Net.

C#  es un lenguaje orientado a objetos, que fue diseñado especialmente para la plataforma .Net (incluso gran parte de las clases del framework están escritas en C#). Como su nombre lo indica, C# está basado en el leguaje C, del que se han eliminado algunos componentes para hacerlo más seguro en cuanto al modelo de ejecución y de administración de memoria.

Visual Basic .Net  es una adaptación para .Net del conocido lenguaje Visual Basic (que a su vez es una adaptación del BASIC original de los años 60) y como tal conserva muchas de las características (y problemas) del leguaje original. Al igual que C#, VB.Net (como también se conoce a Visual Basic .Net) está basado en el paradigma de Orientación a Objetos que estudiaremos más adelante.

Hay una familia de identificadores que forman parte del lenguaje de programación y que sólo pueden usarse con los fines para los que fueron diseñados. Estos identificadores reciben el nombre de palabra clave. Por ejemplo, el lenguaje C# tiene la palabra clave using, con una semántica determinada, y al ser palabra clave, no podremos usarla en otro contexto.

VARIABLES

En cualquier programa que hagamos, necesitaremos manejar datos: el nombre del usuario, el saldo de una cuenta corriente, la cantidad de veces que iteramos sobre una línea de código. Y es natural que estos datos cambien de valor durante la vida del programa. Así, nos encontramos con uno de los conceptos más básicos de la programación, el concepto de variable. Una variable es una referencia a un dato cuyo valor puede cambiar durante la ejecución de un programa.

Siendo un poco más formales, podemos decir que una variable es una posición en la memoria de la computadora, donde reside un dato significativo para el programa. Para que resulte más fácil la lectura y la comprensión de los programas, las variables reciben nombres formados por identificadores válidos. Así, por ejemplo, en el caso de la implementación del algoritmo de Euclides que hemos estudiado, usamos dos variables, denominadas A y B. Es una buena práctica de programación dar nombres significativos a las variables, de manera de hacer evidente la intención que tenemos al momento de definirla. Si en un programa llamamos a una variable c, alguien que lea luego el programa (incluso podríamos ser nosotros mismos) no podrá entender rápidamente cuál es el dato que representa; mientras que si usamos nombreCliente, está claro que, en la ubicación de memoria referenciada por la variable, estamos almacenando el nombre de un cliente.

LA PLATAFORMA .NET

Microsoft .Net es una plataforma para desarrollo y ejecución de software preparada para enfrentar los nuevos desafíos en el mundo de la programación de aplicaciones. Esta plataforma consiste de una familia de lenguajes de programación, una librería de clases común a todos los lenguajes de la plataforma y un entorno de ejecución controlado. Una de las características más importantes de .Net es que el código no se traduce directamente a instrucciones máquina, sino a un lenguaje intermedio que luego, durante le ejecución del programa, es traducido a código máquina. Esto brinda la posibilidad de escribir programas independientes de la plataforma de hardware y del sistema operativo. Además, la biblioteca de clases comunes, junto con una especificación común a todos los lenguajes (conocida como CLS o Common Laguage Specification, Especificación de Lenguaje Común), permite una perfecta y fácil interacción entre diferentes lenguajes de la plataforma.


Según Google Trends, existe una tendencia a usar más el lenguaje C# que VB.Net, además de haber más noticias en Internet sobre C# (fuente www.google.com/trends?q=visual+basic%2Cc%23).

Programación Estructurada

En este aoartado, nos adentraremos en los principios fundamentales de la programación y, al mismo tiempo, iremos aprendiendo sintaxis de los dos principales lenguajes de la plataforma .NET.

MEJORAR EL CÓDIGO

En la primera entrega, vimos que un programa es la implementación de un algoritmo determinado, es decir, una secuencia de pasos o instrucciones, mediante un lenguaje de programación. Cuando escribimos un programa, es normal que necesitemos alterar la secuencia de instrucciones siguiendo determinadas pautas. Si recordamos el algoritmo que analizamos en el capítulo anterior, podemos ver que en cada vuelta, la instrucción por ejecutar depende de si A es mayor que B, o B es mayor que A. En los primeros lenguajes de programación (como el BASIC original), la única forma que existía de variar las instrucciones por ejecutar según una condición era mediante un salto a otra parte del programa (generalmente, a otra posición de memoria u otro número de línea). Es decir, debíamos evaluar la condición y, dependiendo de si ésta era verdadera o falsa podíamos seguir en la línea de abajo o hacer que la próxima instrucción por ejecutar fuera diferente, ubicada en otra parte del programa.

Por ejemplo, para resolver el algoritmo de Euclides usando saltos, deberíamos escribir algo así (con pseudocódigo):

Si A <= 0 Ir a línea 7
Si A > 0 Ir a línea 5
B := B – A
Ir a línea 1
A := A – B
Ir a línea 1
Devolver A

Esta implementación tiene dos grandes problemas. El primero es que su escritura se vuelve un poco dificultosa, porque hay que conocer los números de línea. Por ejemplo, al escribir la línea 1, ya hay que saber que vamos a saltar a la línea 7. Si bien esto podría solucionarse usando números de línea que vayan, por ejemplo, de 10 en 10 y hacer los saltos a números más bien lejanos, a medida que necesitemos modificar el algoritmo y escribir más líneas de código (incluso entre dos líneas ya existentes), los números disponibles se irían acercando más y más, y podría llegar el momento en que necesitáramos renumerar las líneas para tener más espacio. El segundo problema reside en la poca claridad del código. Para alguien que lo lee por primera vez, puede resultar extremadamente difícil entender.

Dados los constantes saltos hacia atrás y hacia adelante a través de las instrucciones del programa, este tipo de código inevitablemente enredado se conoce como código spaghetti, y la instrucción más famosa para hacer saltos de línea es GOTO (del inglés go to, ir a).

HACIA LA PROGRAMACIÓN ESTRUCTURADA

Con el paso del tiempo, los programadores vieron que el código spaghetti resultaba muy difícil de mantener, ya sea para agregar una nueva característica al programa o para corregir un error. Así, en la década del 60, un científico de los Países Bajos, Edsger Dijkstra, formuló un teorema en el que demostró que cualquier programa informático puede escribirse usando sólo tres tipos de instrucciones básicas: la secuencia, la evaluación de condiciones y la repetición o bucle de instrucciones, y sin utilizar instrucciones GOTO. Este teorema, conocido como el Teorema de Dijkstra, sentó las bases de lo que hoy conocemos como programación estructurada y de una gran familia de lenguajes de programación basados en este concepto.

Al escribir un programa utilizando las tres estructuras propuestas por Dijkstra,  se obtienen programas mucho más claros, fáciles de entender y de mantener ya que, en todo momento, la lógica está a la vista, y resulta fácil encontrar cuál es la siguiente instrucción por ejecutar.


En esta figura, vemos las tres estructuras de control propuestas por Dijkstra en el algoritmo de Euclides.

Fundamentos: Algoritmos

Según el diccionario, un algoritmo es un conjunto ordenado de operaciones sistemáticas que permite hacer un cálculo y hallar la solución de un tipo de problemas. Por ejemplo, cualquiera de los métodos que aprendimos en la escuela para multiplicar dos números reales es un algoritmo. El método o la receta para preparar una comida no es un algoritmo, ya que el orden de algunos pasos, muchas veces, no importa o ni siquiera está claramente especificado (por ejemplo, podemos batir las claras a nieve antes o después de mezclar la harina con él azúcar, y aún así vamos a poder hacer una torta).

Ahora que conocemos los algoritmos, podemos redefinir el término programa como implementación de un algoritmo determinado en un lenguaje de programación. El conocimiento del concepto de algoritmo es fundamental para todo programador ya que, en la tarea diaria de escribir programas para resolver problemas, antes debemos descubrir y entender cuál es el algoritmo que los resuelve. Muchas veces, como programadores, nos encontraremos siguiendo los pasos de un algoritmo con lápiz y papel para entender su funcionamiento o probar su eficacia.

LIBROS ÚTILES

Hay una gran cantidad de libros para introducirse en el mundo de la programación de software y de los algoritmos. Recomendamos, Metodología de la Programación. Algoritmos, diagramas de flujo y programas, de Osvaldo Cairo. En el cual, se proponen distintas técnicas de análisis para enfrentar la solución de un problema y llevarla a un algoritmo.

EXPRESIÓN DE ALGORITMOS

Como los algoritmos no están relacionados únicamente con la programación de computadoras, es necesario contar con métodos independientes para expresarlos y, por lo tanto, transmitirlos a otras personas. En la actualidad, existen varias formas de expresar un algoritmo, como ser, el lenguaje natural, el pseudocódigo, los diagramas de flujo, algunos diagramas del lenguaje de modelado UML (que veremos en el último capítulo de este curso), etcétera.

En el caso del lenguaje natural, el algoritmo es expresado por medio de oraciones en un idioma determinado. A modo de ejemplo, se puede indicar el algoritmo, en lenguaje natural, para ver una película en DVD de la siguiente manera: presione la tecla Open/Close para abrir la bandeja del reproductor; luego, inserte el disco y presione la tecla nuevamente. En su televisor, seleccione la opción DVD y por último presione la tecla Play.

CARACTERÍSTICAS DE LOS ALGORITMOS

El estadounidense Donald Knuth, autor del libro El arte de programar computadoras, señaló cinco propiedades que deberá tener todo algoritmo:

> Finito:  un algoritmo debe tener un número finito de pasos, tras los cuales debe terminar.

> Preciso:  cada paso debe estar definido con precisión, rigurosidad y sin ambigüedades.

> Entradas:  todo algoritmo debe tener cero o más entradas, que son los datos que se le proporcionan como información variable y específica de la instancia del problema que resuelve.

> Salida:  un algoritmo tiene una o más salidas, que son el resultado del problema que intenta resolver, y que dependen de las entradas provistas.

> Eficacia:  los pasos deben ser suficientes para lograr el cometido del algoritmo, es decir, el algoritmo debe ser eficaz.

Pseudocódigo

Uno de los problemas del lenguaje natural es que suele resultar excesivamente verborrágico, ya que debe respetar en cierta medida las reglas gramaticales del lenguaje. Para evitar este problema, se puede utilizar un pseudocódigo, que consiste en una serie de normas sintácticas y gramaticales, parecidas a las de un lenguaje de programación, pero sin tanta rigidez, y con cierta libertad en el uso y en la combinación de las palabras.

Por ejemplo, supongamos que debemos escribir el algoritmo de Euclides para encontrar el máximo común divisor entre dos números enteros. Usando pseudocódigo, podemos consignar:

Entrada: A, B enteros
Salida: el máximo divisor común entre A y B

Mientras A > 0 hacer:
      Si A > B
         A := A – B
      Si no
         B := B – A
El Máximo Común Divisor es el contenido de A

Esto significa que la entrada al programa consiste de números enteros (A y B) y, mientras el valor de A sea mayor que cero, se le asigna a A la resta entre A y B, si A es mayor que B; si no, a B se la asigna la diferencia entre B y A. El resultado es el valor que queda en A luego de las sucesivas restas.

Como puede verse en el ejemplo, se especifican claramente cuáles son los datos de entrada y de salida. Además se utilizan algunos símbolos ya conocidos (como el signo mayor a) y otros propios del pseudocódigo (como el signo := para representar que el elemento de la izquierda toma como valor el resultado de la operación de la derecha).

Diagrama de flujo

Los diagramas de flujo son una representación gráfica de los pasos de un algoritmo. En un diagrama de flujo, cada tipo de figura tiene su significado. Su nombre se debe a que las figuras se conectan con flechas que indican la secuencia o flujo de operación.


Si bien muchas personas utilizan sus propios símbolos y figuras al momento de crear sus diagramas de flujo, actualmente está definido de manera clara y estándar cuáles son las figuras válidas y cuál es su significado, de manera que cualquiera que las conozca pueda interpretar el diagrama.

Los símbolos más utilizados son:

> Flecha: indica el sentido del proceso, es decir, hacia dónde hay que dirigirse para encontrar el siguiente paso.
> Rectángulo: se usa para representar un paso determinado del algoritmo.
> Rombo: representa un punto de decisión sobre la base de una condición. De un rombo salen siempre dos flechas: una en un sentido, si se cumple la condición y otra en otro sentido, si la condición no se cumple.

PARA TENER EN CUENTA

Al hacer un diagrama de flujo, hay que tener muy presente estos aspectos:

- Debe haber un único punto de inicio del proceso.
- Debe haber siempre un camino para llegar a la solución.
- Debe haber un único punto de fin del proceso.

Para ilustrar el concepto, vamos a especificar el algoritmo de Euclides que usamos anteriormente, mediante un diagrama de flujo.

Diagrama de flujo del algoritmo de Euclides

CONCLUSIONES

Para introducirse en el mundo de la programación y el desarrollo de aplicaciones de software, es muy importante comenzar con una buena base de conocimiento de algoritmos y técnicas de resolución de problemas de distinto tipo, sin usar un lenguaje específico (podemos usar pseudocódigo). Una vez que dominemos ciertas técnicas de resolución de problemas y podamos expresar las soluciones como algoritmos, estaremos preparados para aprender lenguajes de programación que nos permitan llevar esos algoritmos a una computadora que los ejecute por nosotros.

Fundamentos: Compilación e interpretación

Cuando escribimos un programa en algún lenguaje, nuestro propósito final siempre será ejecutarlo en una computadora para realizar una tarea específica. Ahora bien, al utilizar un lenguaje de programación estamos escribiendo instrucciones que no son exactamente las que la computadora entiende. Para que nuestro programa pueda ser ejecutado, será necesario traducir las instrucciones que hemos escrito en forma más sencilla para que la computadora pueda entenderlas. Esta traducción resulta siempre necesaria para poder ejecutar un programa escrito en cualquier lenguaje. El tipo de traducción que se haga, permitirá clasificar los lenguajes en lenguajes compilados y lenguajes interpretados.

En los lenguajes compilados, la traducción se realiza por única vez, almacenando las instrucciones ya traducidas a lenguaje máquina en un archivo. De este modo, al momento de ejecutar el programa, la computadora lee una a una las instrucciones del archivo generado durante el proceso de compilación, y las ejecuta. Para realizar la traducción o compilación, se utiliza un programa llamado compilador, que se encarga de tomar cada una de las instrucciones escritas en un lenguaje de programación y traducirlas a una o más instrucciones del lenguaje de la máquina. Durante la compilación, el compilador además valida que las instrucciones que hemos escrito sean correctas y que se respeten todas las reglas del lenguaje.


Por otro lado, con los lenguajes interpretados, no existe un paso previo de compilación, sino que la traducción se realiza mientras se ejecuta el programa. Para ello, se utiliza un software denominado intérprete, que a medida que el programa se ejecuta, lee cada una de las instrucciones y las traduce a instrucciones de la máquina, para que sean ejecutadas. Al igual que los compiladores, los intérpretes deben validar que las instrucciones estén bien escritas para poder traducirlas sin problemas. Generalmente, usando lenguajes compilados, se logra mayor rendimiento, ya que  el proceso de validación y de traducción se realiza una sola vez (al momento de la compilación). Luego, una vez que el programa está compilado, cada ejecución se hace sobre una secuencia de instrucciones de la computadora. Al usar lenguajes interpretados, la validación y traducción se realiza cada vez que se ejecuta el programa. Es más, si una misma instrucción se repite varias veces, el intérprete la validará y traducirá cada vez. Sin embargo, los lenguajes interpretados proveen cierto grado de flexibilidad a la hora de realizar cambios en nuestro programa ya que, modificando cualquier instrucción, estará lista para que el intérprete la tome y la traduzca.

Fundamentos: Los lenguajes de programación

Ahora que sabemos qué es un programa, podemos decir que la programación es el proceso de construir programas. Para escribir programas, necesitamos conocer la lista de las posibles instrucciones que debemos proporcionar a la computadora, y cómo combinarlas para lograr los resultados deseados.

De acuerdo con el primer párrafo, para confeccionar programas, debemos escribir una secuencia de instrucciones que puedan ser entendidas por la computadora, pero como vimos anteriormente, hasta las computadoras más modernas trabajan con el sistema binario, por lo que, para programarlas, deberíamos proporcionarles secuencias de unos y ceros formando las instrucciones de nuestro programa. Si bien el sistema binario es muy simple, para un ser humano, resultaría imposible recordar las combinaciones de unos y de ceros que forman cada una de las instrucciones. Para solucionar este problema, existen los lenguajes de programación, una forma más sencilla y legible de representar las instrucciones.

Sin entrar en detalles formales, podemos decir que un lenguaje de programación es un conjunto de reglas que determinan, de forma clara, precisa y sin ambigüedades, la forma en que se le imparten las instrucciones a una computadora para construir un programa. Estas reglas se dividen en reglas sintácticas y reglas semánticas. Las reglas sintácticas especifican cuáles son los caracteres válidos del lenguaje y cómo se pueden agrupar en palabras también válidas. Las reglas semánticas determinan cuál es el significado de las palabras, es decir, qué se espera que la computadora haga cuando recibe una palabra o una instrucción.

ARQUITECTURAS CISC Y ARQUITECTURAS RISC

El conjunto de instrucciones que puede entender una computadora depende de su arquitectura (por ejemplo, un procesador Intel tiene un conjunto de instrucciones diferente del de un procesador Motorola). La cantidad de instrucciones permiten clasificar a los procesadores en dos grandes grupos: CISC (Complex Instruction Set Computer, computadora con conjunto de instrucciones complejo) y RISC (Reduced Instruction Set Computer, computadora con conjunto de instrucciones  reducido).

Los lenguajes de programación fueron creados para facilitar la escritura de programas, proveyendo una abstracción de las instrucciones reales de la computadora (formadas por unos y ceros) y reemplazándolas por palabras que fueran más fáciles de recordar por las personas. Según el nivel de abstracción que proveen, los lenguajes de programación se clasifican en tres niveles. Cuanto más alto sea el nivel, más cercanas al lenguaje humano serán sus instrucciones y, generalmente, más poderosas, ya que cada instrucción del lenguaje puede representar operaciones complejas formadas por muchas instrucciones de la máquina.

TIPOS DE LENGUAJES

BAJO NIVEL: Las instrucciones del lenguaje están muy relacionadas con las instrucciones de la computadora, por lo tanto, el programador debe tener un buen conocimiento del funcionamiento del equipo. Al programar directamente (o casi) con instrucciones de la máquina, se obtienen resultados eficientes y se puede lograr cualquier cosa que haga la computadora, aunque con un esfuerzo más grande que con otros niveles de abstracción. Como lenguaje de bajo nivel, podemos mencionar el Assembler.

MEDIO NIVEL: Si bien algunos autores ignoran este nivel, existe un grupo de lenguajes con una abstracción un poco más alta que los de bajo nivel, pero aún bastante cercana al hardware. Su sintaxis es más sencilla que la de los lenguajes de bajo nivel, pero permiten escribir programas cercanos al lenguaje de la computadora. Un ejemplo típico de lenguaje de nivel medio es el lenguaje C.

ALTO NIVEL:  Este lenguaje está formado por palabras comunes en algún idioma (como por ejemplo el inglés), por lo que resultan fáciles de recordar y de interpretar. Generalmente, son lenguajes más poderosos en cuanto a expresividad que los de bajo nivel, pero puede ocurrir también que resulte difícil implementar programas que tengan que hacer un uso muy específico del hardware. Como ejemplo de lenguajes de alto nivel, podemos mencionar a C# o Java.

Es muy importante tener en cuenta que, aunque los lenguajes de más alto nivel puedan parecerse al lenguaje natural de los seres humanos, deberán ser más estrictos y limitados, ya que es necesario que permitan definir las instrucciones de una forma entendible y sin ningún tipo de ambigüedad.

LOS LENGUAJES EN LA ACTUALIDAD

A lo largo del tiempo, los lenguajes han evolucionado y se adaptaron a las necesidades y a las posibilidades computacionales de cada momento. Por eso, actualmente disponemos de una gran cantidad de lenguajes de programación entre los cuales elegir a la hora de comenzar un desarrollo o de aprender una nueva tecnología.


Para tomar una buena decisión y elegir el lenguaje que más nos conviene, es importante conocer sus características, sus capacidades y debilidades. Además, un aspecto fundamental para tener en cuenta es el paradigma o enfoque del lenguaje, es decir, de qué forma hay que pensar y escribir el programa para resolver un problema. Los paradigmas más conocidos son el imperativo, el declarativo y el orientado a objetos. Mediante el paradigma imperativo, debemos indicar explícitamente los pasos por seguir para resolver el problema, es decir, debemos indicar el cómo. Por otro lado, el paradigma declarativo permite escribir el programa describiendo las características del problema, es decir, especificando el qué. Por último, el paradigma orientado a objetos permite modelar y escribir los programas a partir de la abstracción de los objetos reales que forman parte del dominio del problema. Actualmente, el paradigma declarativo es usado en el ambiente académico y con fines de investigación, mientras que, en ambientes productivos y de negocios, se utilizan los lenguajes imperativos y, en mayor medida, los orientados a objetos.

QUÉ ES EL ANÁLISIS LÉXICO?

La compilación de un programa es una tarea bastante compleja, que involucra una serie de pasos conducentes a la generación del código de máquina. El primer paso se denomina análisis léxico. Consiste en leer el texto del programa (normalmente almacenado en uno o en varios archivos) y aplicar ciertas reglas para identificar las palabras clave y las expresiones válidas del lenguaje de programación. Durante el análisis léxico, ya pueden identificarse algunos errores básicos, frecuentes en la mayoría de los programas, como por ejemplo, los identificadores mal formados.

Fundamentos: ¿Qué es el software?

En esta primera entrega, veremos los fundamentos básicos del software, su funcionamiento, su uso y su creación. A su vez introduciremos los principales conceptos sobre la programación moderna.

Desde siempre, las computadoras han sido máquinas con la única capacidad de llevar a cabo instrucciones, como imprimir un texto en un dispositivo de salida o sumar dos números. Un programa es un conjunto de instrucciones y datos que juntos y, de manera sistemática, permiten resolver problemas. Podemos entonces definir el software como el conjunto de programas que funcionan en una computadora y que permiten realizar una o varias tareas específicas.

Es importante resaltar que, al hablar de computadoras, no nos referimos sólo a las personales (PC), sino a cualquier dispositivo capaz de leer instrucciones de una memoria, y ejecutarlas. Por lo tanto, podemos encontrar software en un lavarropas, en un respirador artificial y hasta en automóviles modernos.

Según su uso, el software se puede clasificar en dos grandes grupos: el software de sistema y el software de aplicación. El software de sistema es el conjunto de programas básicos para el funcionamiento de la computadora, como por ejemplo el sistema operativo (Windows o Linux), los drivers, etcétera., mientras que el software de aplicación son los programas para realizar tareas específicas, como un procesador de texto, un juego o un compilador.

Los términos software y sistema se utilizan para referirse a lo mismo. Sin embargo, la palabra sistema por sí sola no tiene nada que ver con el software. Un sistema es un conjunto de elementos que interactúan de alguna manera, como puede ser el sistema digestivo o el sistema solar. Otro ejemplo de sistema, son los sistemas de información, sin que esto tampoco implique un software. Por ejemplo, un sistema contable es un conjunto de métodos y de herramientas que permiten mantener la información sobre los movimientos económicos y los bienes de una empresa, pero se pueden utilizar libros en papel para alcanzar el objetivo. Finalmente, un sistema de información basado en computadora es la implementación con herramientas computacionales (programas y datos) de un sistema de información. Por lo tanto, como sinónimo de software podemos utilizar sistema de información basado en computadora. Por otro lado, los términos programa y sistema suelen utilizarse indistintamente, pero no está bien. Generalmente, podemos decir que un sistema es un grupo de programas que interactúan para realizar ciertas tareas. Un programa es una unidad mucho más pequeña, independiente y sencilla que un sistema.

EL HARDWARE

El otro componente importante de una computadora es el hardware, que incluye todos los elementos físicos o materiales que la conforman. La diferencia entre el hardware y el software es que este último es intangible (lo vemos, pero no podemos tocarlo), mientras que el hardware es totalmente palpable.

UN POCO DE HISTORIA

Muchos autores coinciden en afirmar que la idea de programa como secuencia de instrucciones se remonta a principios del siglo XIX y no tiene nada que ver con la computación. Efectivamente, en 1801 un francés llamado Joseph Marie Jacquard ideó un mecanismo de tarjetas perforadas para controlar los dibujos que formaban los hilos en una máquina para tejer. De esa manera, lograba programar las puntadas de la máquina para obtener tramas y figuras repetibles.

En 1843, Ada Augusta Lovelace, hija del poeta inglés Lord Byron, planteó la idea de usar tarjetas perforadas para controlar la Máquina Diferencial de Babbage y lograr que repita ciertas operaciones. Unos años más tarde, su idea fue tomada para desarrollar un sistema de cómputo para la oficina de censos de los Estados Unidos. Las tarjetas estaban diseñadas de tal modo que los agujeros representaban la edad, raza, sexo, etcétera. Este desarrollo permitió que el tiempo en obtener los resultados del censo de 1890 fuera de 5 años menos que el censo anterior.

La idea de lady Ada tuvo tal repercusión que, al día de hoy, se la considera como la primera programadora, y las tarjetas perforadas fueron utilizadas en centros de cómputos hasta no hace mucho tiempo.

LA MÁQUINA DIFERENCIAL

En el año 1812, Charles Babbage, preocupado por los errores de cálculo en las tablas matemáticas, pensó que sería útil poder calcularlos de forma automática. Ideó, entonces, una máquina capaz de obtener aproximaciones al resultado de una función matemática, mediante un método llamado de las diferencias. Si bien por limitaciones propias de la época nunca llegó a terminar su construcción, su denominada Máquina diferencial sentó las bases de la computación moderna.

Ya en el siglo XX, el físico estadounidense John Atanasoff, conocedor de las teorías de Babbage y consternado por la cantidad de cálculos que debía realizar, pensó en construir una máquina de cálculo que, a diferencia de las mecánicas, sería digital, y su funcionamiento se basaría en el sistema binario. Su aparato fue conocido como ABC Atanasoff-Berry-Computer, y por eso es considerado el iniciador de la computación digital.

Luego, durante la Segunda Guerra Mundial, se construyó y comenzó a funcionar en instalaciones militares de los Estados Unidos una máquina llamada ENIAC (Electronic Numeric integrator and Computer). Su funcionamiento se basaba en tubos de vacío, interruptores y relés para hacer operaciones matemáticas utilizando el sistema binario. Por su tamaño, ocupaba una habitación entera.


La ENIAC fue una de las primeras computadoras del siglo XX. Su poder de cálculo era menor al de una calculadora de bolsillo actual.

A partir de la ENIAC, las computadoras fueron evolucionando año tras año, a un ritmo cada vez más vertiginoso hasta llegar a las computadoras que conocemos en la actualidad. Sin embargo, a pesar de esta evolución, las computadoras mantienen dos características esenciales: están basadas en el sistema binario y necesitan que se les provea de una secuencia ordenada de instrucciones para poder funcionar.

LA MÁQUINA DE TURING

Creada por Alan Turing, consistía en una cinta y un cabezal que podía leer y escribir caracteres en dicha cinta, como también moverse hacia la izquierda y hacia la derecha. La cinta podría verse como el conjunto de entradas y salidas de un programa. Turing demostró que su máquina podría resolver cualquier problema representable por un algoritmo y que, si no era capaz de resolverlo, entonces resultaba, insoluble en una computadora. Su trabajo se convirtió en el tema central de estudio de la Teoría de la Computación.