3 Problemas, algoritmos y lenguajes de programación

Como seres humanos, tenemos incorporada intuitivamente la resolución de problemas cotidianos gracias a nuestra experiencia, y para intentar afrontar un inconveniente, solemos hacer un proceso rápido de selección e intentamos buscar la opción más favorable.

Un algoritmo es una estrategia consistente de un conjunto ordenado de pasos que nos lleva a la solución de un problema o alcance de un objetivo. Un algoritmo cumple con las siguientes características:

  • Está expresado de manera clara y precisa: el lector debe poder entender sin ambigüedades cuáles son los pasos involucrados.
  • Es efectivo: en el sentido de que sea factible llevar a cabo dichos pasos.
  • Es finito: debe arrojar una respuesta o brindar la solución al problema en una cantidad finita de tiempo, es decir, termina luego de un número acotado de pasos.

Los algoritmos, así como los problemas que intentan resolver, varían ampliamente en complejidad. Algunos problemas son tan simples que inmediatamente se nos ocurre un algoritmo apropiado para su resolución. Para problemas complejos, en cambio, desarrollar un algoritmo requiere más tiempo y razonamiento, e incluso se pueden originar distintos algoritmos para solucionar un mismo problema.

La resolución computacional de un problema consiste de dos etapas básicas:

  • Diseño algorítmico: desarrollar un algoritmo, o elección de uno existente, que resuelva el problema.
  • Codificación: expresar un algoritmo en un lenguaje que la computadora pueda interpretar.

Al aprender sobre programación, comenzamos enfrentándonos a problemas simples para los cuales la primera etapa parece sencilla, mientras que la codificación se torna dificultosa ya que hay que aprender las reglas del lenguaje de programación. Sin embargo, mientras que con práctica rápidamente podemos ganar facilidad para la escritura de códigos, el diseño algorítmico se torna cada vez más desafiante al encarar problemas más complejos. Es por eso que haremos incapié en el planteo y desarrollo de algoritmos como una etapa fundamental en la programación.

3.1 El diseño algorítmico

Frente a cada problema, el primer paso es idear una algoritmo para su solución y expresarlo por escrito, por ejemplo, en español, pero adaptando el lenguaje humano a formas lógicas que se acerquen a las tareas que puede realizar una computadora. En programación, el lenguaje artificial e informal que usan los desarrolladores en la confección de algoritmos recibe el nombre de pseudocódigo. Es la herramienta que utilizamos para describir los algoritmos mezclando el lenguaje común con instrucciones de programación. No es en sí mismo un lenguaje de programación, es decir, la computadora no es capaz de entenderlo, sino que el objetivo del mismo es que el programador se centre en la solución lógica y luego lo utilice como guía al escribir el programa.

El pseudocódigo, como cualquier otro lenguaje, está compuesto por:

  • Un léxico: conjunto de palabras o frases válidas para escribir las instrucciones
  • Una sintaxis: reglas que establecen cómo se pueden combinar las distintas partes
  • Una semántica: significado que se les dá a las palabras o frases

Además el pseudocódigo, y toda codificación en lenguajes de programación, sigue una estructura secuencial: define una acción o instrucción que sigue a otra en secuencia. Esta estructura puede representarse de la siguiente forma:

ALGORITMO: "Ejemplo"
COMENZAR
    Acción 1
    Acción 2
    ...
    Acción N
FIN

Se comienza con un título que describa el problema que el algoritmo resuelve, seguido por la palabra COMENZAR. Luego se detallan las acciones o instrucciones a seguir y se concluye con la palabra FIN. Por ejemplo, si nuestro problema es poner en marcha un auto, el algoritmo para resolverlo puede ser expresado mediante el siguiente pseudocódigo:

ALGORITMO: "Arrancar el auto"
COMENZAR
    INSERTAR la llave de contacto
    UBICAR el cambio en punto muerto
    GIRAR la llave hasta la posición de arranque
    SI el motor arranca 
        ENTONCES
            DEJAR la llave en posición "encendido"
        SI NO
            LLAMAR al mecánico
    FINSI
FIN

Es importante destacar la presencia de sangrado (indentación), instrucciones, verbos y estructuras de control en el ejemplo anterior.

3.2 Codificación: creación y edición de programas

El algoritmo anterior está presentado en pseudocódigo utilizando el lenguaje español, una opción razonable para compartir esta estrategia entre personas que se comuniquen con este idioma. Claramente, si queremos presentarle nuestro algoritmo a alguien que sólo habla francés, el español ya no sería una buena elección, y mucho menos si queremos presentarle el algoritmo a una computadora.

Para que una computadora pueda entender nuestro algoritmo, debemos traducirlo en un lenguaje de programación: un idioma artificial diseñado para expresar cómputos que puedan ser llevados a cabo por equipos electrónicos, es decir es un medio de comunicación entre el humano y la máquina. Ejemplos de lenguajes de programación son Fortran, BASIC, C++ o Java. En este curso, aprenderemos a utilizar un lenguaje incorporado en el software estadístico SAS: IML (Interactive Matrix Language).

Distintos lenguajes de programación y sus logos.

Figura 3.1: Distintos lenguajes de programación y sus logos.

Cada una de las acciones que componen al algoritmo son codificadas con una o varias instrucciones o sentencias, expresadas en el lenguaje de programación elegido, y el conjunto de todas ellas constituye un programa. El programa se guarda en un archivo con un nombre generalmente dividido en dos partes por un punto, por ejemplo: miPrimerPrograma.sas. La primera parte es la raíz del nombre con la cual podemos describir el contenido del archivo. La segunda parte es indicativa del uso del archivo, por ejemplo, .sas indica que contiene un programa escrito en el lenguaje de SAS. El proceso general de ingresar o modificar el contenido de un archivo se denomina edición.

Existen distintos tipos de lenguajes de programación que cumplen la función de intermediarios entre el desarrollador y el hardware. Simplificando esta gran variedad, podemos decir que hay dos grupos generales. Por un lado, se encuentran los lenguajes más próximos a la arquitectura del hardware, denominados lenguajes de bajo nivel, que son más rígidos y complicados de entender para nosotros. Por otro lado, están aquellos más cercanos a los programadores y usuarios, denominados lenguajes de alto nivel, diseñados para que sea fácil para los humanos expresar los algoritmos sin necesidad de entender en detalle cómo hace exactamente el hardware para ejecutarlos. El lenguaje que utilizaremos en este taller, IML, es de alto nivel.

Para que un programa escrito en un lenguaje de alto nivel pueda ser ejecutado, se necesita de compiladores o intérpretes que básicamente lo traducen al lenguaje de bajo nivel que es apropiado para el hardware que se dispone. Un compilador toma un programa escrito en lenguaje de alto nivel y produce como resultado un programa escrito en otro lenguaje, que luego puede ser ejecutado, mientras que un intérprete traduce y ejecuta simultáneamente.

3.3 Errores de programación y depuración

Tal como el lenguaje humano, los lenguajes de programación tienen su propio vocabulario y su propia sintaxis, que es el conjunto de reglas gramaticales que establecen cómo se pueden combinar las distintas partes del lenguaje. Estas reglas sintácticas determinan que ciertas sentencias están correctamente construidas mientras otras no.

Cuando compilamos un programa, el compilador primero chequea si el mismo es sintácticamente correcto. Si hemos violado alguna regla, mostrará un mensaje de error y debemos editar nuestro programa para corregirlo.

Aunque los errores de sintaxis pueden ser frustrantes, los errores lógicos suelen ser los más problemáticos. Por ejemplo, el programa puede ser compilado sin errores pero arroja resultados incorrectos o ningún resultado. En estos casos hay que revisar el programa para encontrar algún error en la lógica del mismo. Este tipo de errores suelen llamarse bugs y la corrección de los mismos debugging (depuración).

En nuestro recorrido por el taller, seguramente nos enfrentaremos a varias situaciones en las cuales nos parece que el algoritmo es correcto, para luego descubrir que falla porque nos hemos olvidado de considerar alguna situación particular. Si bien esto puede resultar desalentador, forma parte de la práctica cotidiana de todos los programadores.

Encontrando un "bug" en un programa.

Figura 3.2: Encontrando un “bug” en un programa.

3.4 Resumiendo

A la hora de resolver un problema computacional, seguiremos los siguientes pasos:

  • Analizar el problema que vamos a resolver.
  • Imaginar una solución (algoritmo).
  • Traducir la solución a pseudocódigo.
  • Implementar en un lenguaje de programación todo lo analizado.
  • Compilar o correr el programa.
  • Realizar pruebas de ejecución.
  • Corregir los errores que haya.