Saltar al contenido

LDP | §1.4 - Los ladrillos básicos de la programación

§1.3 Clasificación elemental de lenguajes informáticos

Hemos mencionado que el estudio del problema de la programación trajo como resultado reconocer ciertas características que todo lenguaje que sea apto para escribir algoritmos debe poseer. Así como sucede en otros campos del conocimiento (por ejemplo, las tres leyes de Newton son un conjunto mínimo de principios necesario para construir la Mecánica), es posible identificar un número mínimo de bloques de construcción necesario para describir cualquier algoritmo. De hecho, un primer paso para familiarizarse con cualquier lenguaje concreto de programación radica en ver cómo se describen estos bloques en ese lenguaje.

Este conjunto mínimo consiste en la secuencia, la decisión y la iteración. Analizaremos cada uno.

Secuencia

Consideren la siguiente descripción:

Receta para cocinar 1/2 kg de fideos

 1. Llenar una olla con 5 litros de agua.
 2. Colocar la olla al fuego.
 3. Llevar a hervor.
 4. Agregar al agua 75 g de sal.
 5. Introducir los fideos en el agua hirviendo.
 6. Cocinar durante el tiempo indicado por el fabricante en el paquete.
 7. Retirar la olla del fuego.
 8. Colar los fideos.
 9. Agregar la salsa.
10. Servir.

Dado que esta descripción nos permite cocinar exitosamente este tipo de pasta con sólo seguir estas instrucciones, podemos legítimamente llamarla algoritmo para hacer fideos e identificar dos cosas:

  • Cada línea de esta descripción es una operación individual o atómica, en el sentido que no se puede (o no necesitamos) descomponerlas en otras operaciones más simples. Por ejemplo, la tarea "agregar la salsa" asume que la salsa está ya hecha y disponible.
  • Las operaciones están escritas en un cierto orden, el cual debe respetarse para lograr el resultado esperado.

Entendemos que cada operación en esta descripción se inicia no antes de haber finalizado la anterior, es decir, no existe superposición temporal de las tareas.

Además, en principio podemos considerar que, en el sentido más general, una tarea puede necesitar ciertas condiciones, información o datos para poder empezar a realizarla, aunque siempre esperaremos que entregue un resultado como consecuencia de su realización. Es decir, las tareas tienen (potencialmente) una entrada (datos) y una salida (resultado).

Por otro lado, debemos notar que al especificar un orden, estamos diciendo que tendremos garantizado el resultado con sólo seguirlo tal como se describe. En algunos casos es posible alterar el orden sin que el resultado cambie (por ejemplo, podría poner la sal en la olla y luego llenarla con agua); esto daría lugar a una receta (algoritmo) diferente para lograr el mismo resultado. De todas formas, debe quedar claro que el algoritmo, al especificar un cierto orden, no se hace responsable del resultado si se lo altera.

Esta estructura nos muestra la construcción más simple posible para un algoritmo: la secuencia.

La secuencia es una sucesión de tareas que consideramos simples (sea por que se trata de algo simple e inmediato, su realización es un problema ya resuelto, o no es necesario descomponer en tareas aún más simples) realizadas de manera secuencial y en el orden indicado, permitiendo que cada una tenga su entrada disponible al momento de su ejecución, y entregue su resultado a la siguiente.

En esta primera receta, cada uno de los puntos 1 a 10 son tareas.

Decisión

La cantidad de sal indicada en la receta es típica entre diferentes chefs (se habla de 15 g de sal por cada litro de agua), pero también se sugiere que si la salsa que vaya a emplearse es perticularmente salada (por caso las de queso azul, anchoas, etc) puede ser conveniente reducir esta cantidad, por ejemplo a 10 g por litro.

Es obviamente posible tener dos recetas, según vaya a emplearse uno u otro tipo de salsa, pero podríamos hacer más "inteligente" nuestra receta si simplemente hacemos que acomode ambos casos en una única "descripción". Así tendríamos una segunda versión:

Receta para cocinar 1/2 kg de fideos (versión 2)

 1. Llenar una olla con 5 litros de agua.
 2. Colocar la olla al fuego.
 3. Llevar a hervor.
 4. Si la salsa a utilizar es "salada", entonces:
        a. Agregar al agua 50 g de sal.
    De lo contrario:
        b. Agregar al agua 75 g de sal.
 5. Introducir los fideos en el agua hirviendo.
 6. Cocinar durante el tiempo indicado por el fabricante en el paquete.
 7. Retirar la olla del fuego.
 8. Colar los fideos.
 9. Agregar la salsa.
10. Servir.

Aunque la nueva receta tiene 10 pasos como la versión previa, el punto 4 se ha convertido ahora en una decisión que, según el resultado de una cierta condición, realiza la tarea a o la tarea b (pero no ambas), tras lo cual las tareas siguientes se realizan según la descripción anterior. Precisemos algunas nociones:

  • La decisión se basa en una condición (en este caso el tipo de salsa) expresada de manera que el resultado es necesariamente una de dos alternativas: verdadero o falso (aquí es: ¿la salsa a utilizar es "salada"?);
  • Si bien en este caso debemos hacer algo tanto en una situación como en la otra, es perfectamente posible que debamos hacer una tarea sólo para uno solo de los resultados de la condición, y no para el otro; en ese caso, no usaríamos la cláusula "De lo contrario". A veces es posible plantear la decisión de ambas formas. Por ejemplo, consideren esta variante:
        ...
     4' Agregar al agua 50 g de sal.
     4" Si la salsa a utilizar no es "salada", entonces:
           a. Agregar al agua 25 g de sal.
     5. Introducir los fideos en el agua hirviendo.
        etc...
    

    Esta variante aprovecha el hecho que, en cualquier caso, la receta exige agregar un mínimo de 50 g de sal; si estamos en el caso de una salsa no salada, deberíamos  haber usado 75 g, para lo cual sólo resta agregar los 25 g de diferencia. Notar además que hemos debido "invertir" la pregunta para poder plantear la decisión de esta forma.

Así como en la práctica aceptamos como un rasgo de inteligencia el hecho que una persona tome una correcta decisión en función de la información a su alcance (y de esa forma pueda "adaptarse" a una cierta situación), también podríamos decir que un algoritmo que cuente con la posibilidad de "decidir" tiene la capacidad de resolver problemas más generales que otros. En este punto, podemos definir:

La decisión es la elección entre dos caminos alternativos de ejecución de tareas, según el resultado de una condición que pueda evaluarse como verdadera o falsa.

Iteración

El fabricante de los fideos establece un tiempo de cocción para que el "punto" alcanzado sea en general del agrado de la mayoría (digamos, un tiempo que asegure que los fideos "no se pasen" ni queden "duros"). Pero siendo el punto exacto de cocción una cuestión de gusto personal en muchos casos, surge el problema de cómo cocinar los fideos para mi gusto.

Además, el tiempo establecido por el fabricante asume implícitamente ciertas condiciones que quizás no se verifiquen cuando llega el momento de cocinarlos. Por ejemplo, ese tiempo puede depender:

  • De la cantidad de agua en relación a la masa de fideos (en caso de no haber respetado exactamente la cantidad de agua sugerida);
  • De la temperatura ambiente (un ambiente frío puede alargar el tiempo de cocción);
  • De la capacidad de calor de la cocina empleada, etc.

Previendo esto, en muchos casos el paquete informa un rango de tiempos de cocción, en lugar de una duración específica. Luego, ¿cómo hago para que la cocción dure el tiempo justo?

Una forma común de resolver esto es mantener la cocción por el tiempo mínimo sugerido, y a partir de ese momento "probar" los fideos desde la cacerola misma hasta que se encuentren en el punto deseado. Con esta idea en mente, podemos mejorar nuestro algoritmo así:

Receta para cocinar 1/2 kg de fideos (versión 3)

 1. Llenar una olla con 5 litros de agua.
 2. Colocar la olla al fuego.
 3. Llevar a hervor.
 4. Si la salsa a utilizar es "salada", entonces:
        a. Agregar al agua 50 g de sal.
    De lo contrario:
        b. Agregar al agua 75 g de sal.
 5. Introducir los fideos en el agua hirviendo.
 6. Cocinar durante el tiempo mínimo indicado por el fabricante en el paquete.
 7. Probar el punto cocción.
 8. Si el punto de cocción es el deseado, continuar a 9.
        a. Esperar un minuto.
        b. Probar el punto de cocción.
        c. Volver a 8.
 9. Retirar la olla del fuego.
10. Colar los fideos.
11. Agregar la salsa.
12. Servir.

En esta nueva descripción, el punto 8 introduce la idea de iteración. En esencia, una o más tareas se repiten (iteran) a la espera de un resultado que (aún) no es el esperado. Ese resultado, al igual que en la decisión, es una condición que debe ser verdadera o falsa, y es responsabilidad del programador asegurarse que, tarde o temprano, la condición de terminación se cumpla: de lo contrario, el algoritmo nunca terminaría.

Notar que, de las tres tareas que se incluyen en la iteración, la última necesariamente debe ser un retorno al punto de verificación. Como esto es necesario para que la iteración tenga sentido, los lenguajes reales que adhieren al paradigma de la programación estructurada usan una sintaxis que hace innecesario (y en muchos casos ni siquiera puede hacerse manualmente) este "retorno explícito": simplemente se vuelve al principio luego de la última tarea de esa iteración. Con esta idea, una versión algo más compacta quedaría así:

Receta para cocinar 1/2 kg de fideos (versión 4)

 1. Llenar una olla con 5 litros de agua.
 2. Colocar la olla al fuego.
 3. Llevar a hervor.
 4. Si la salsa a utilizar es "salada", entonces:
        a. Agregar al agua 50 g de sal.
    De lo contrario:
        b. Agregar al agua 75 g de sal.
 5. Introducir los fideos en el agua hirviendo.
 6. Cocinar durante el tiempo mínimo indicado por el fabricante en el paquete.
 7. Probar el punto cocción.
 8. Mientras el punto de cocción no es el deseado:
        a. Esperar un minuto.
        b. Probar el punto de cocción.
 9. Retirar la olla del fuego.
10. Colar los fideos.
11. Agregar la salsa.
12. Servir.

Definimos entonces:

Una iteración (también llamado bucle) es una secuencia de instrucciones que se ejecuta repetidamente mientras se cumple (o hasta que se cumpla) una determinada condición.

Debe notarse que es fundamental que la condición que permite a la iteración terminar eventualmente se cumpla, ya que de lo contrario el programa podría no terminar nunca (salvo alguna maniobra externa como suspender el programa, reiniciar el proceso o el equipo que lo estaba ejecutando, etc.). La verificación que una iteración termine es resposabilidad del programador.

Notemos además que verificamos la condición de terminación antes de entrar a la iteración por primera vez. Podría suceder que al primer intento el punto de cocción sea el deseado, en cuyo caso la iteración no llega a ejecutarse. Esta forma particular se llama iteración con pregunta al principio y permite que una iteración se ejecute cero o más veces.

Como contraste, consideremos esta variante:

Receta para cocinar 1/2 kg de fideos (versión 4 bis)

 1. Llenar una olla con 5 litros de agua.
 2. Colocar la olla al fuego.
 3. Llevar a hervor.
 4. Si la salsa a utilizar es "salada", entonces:
        a. Agregar al agua 50 g de sal.
    De lo contrario:
        b. Agregar al agua 75 g de sal.
 5. Introducir los fideos en el agua hirviendo.
 6. Cocinar durante el tiempo mínimo indicado por el fabricante en el paquete.
 7. Repetir:
        a. Esperar un minuto.
        b. Probar el punto de cocción.
    Mientras el punto de cocción no es el deseado.
 8. Retirar la olla del fuego.
 9. Colar los fideos.
10. Agregar la salsa.
11. Servir.

En este caso hemos trasladado la condición al final de la iteración. La principal diferencia de este esquema con el anterior es que la iteración se ejecuta el menos una vez. Observemos que en esta variante la cocción durará al menos el mínimo indicado por el fabricante + un minuto.

La elección de una u otra forma dependerá ante todo (además de la naturaleza del algoritmo) de las posibilidades del lenguaje de programación elegido. Si bien todos implementan la iteración con pregunta al principio, sólo unos pocos implementan esta variante llamada iteración con pregunta al final, como por ejemplo Pascal, Visual Basic y sus derivados (VBA, VB.NET, VBScript), etc. En esos casos suele ser posible también escribir la iteración con una condición de continuación (mientras se cumpla cierta condición) o con una condición de terminación (hasta que se cumpla cierta condición). Sólo hay que prestar atención al hecho que la condición de continuación es la opuesta lógica de la de terminación; por ejemplo,

Mientras el punto de cocción no es el deseado

equivale a

Hasta que el punto de cocción es el deseado


[1] La razón es que, mientras una iteración con pregunta al final puede simularse con una iteración con pregunta al principio, la recíproca no es cierta.