En este capítulo se hace una breve introducción a los algoritmos, las diferentes formas de expresarlos, así como la forma de implementarlos (codificarlos) en Javascript.
Un algoritmo describe la secuencia lógica de acciones que deben llevarse a cabo para resolver un problema, de manera que siempre se logre la solución correcta. Cuando existen múltiples algoritmos para resolver un mismo problema (que es el caso más frecuente) el mejor algoritmo es aquel que resuelve el problema en el menor tiempo.
Los bloques fundamentales para la construcción de algoritmos son la secuencia, es decir el conjunto de instrucciones que se ejecutan una a continuación de otra, la selección y la iteración.
Hasta ahora, los problemas han sido resueltos empleando únicamente la secuencia, en capítulos posteriores se estudiarán los bloques fundamentales restantes.
Los algoritmos pueden ser expresados en diferentes formas, siendo los más usuales: el lenguaje natural, pseudocódigo, código documentado y los diagramas de flujo.
El lenguaje natural es una de las formas más empleadas para expresar algoritmos. Esta forma ya ha sido empleada en la resolución de problemas en los anteriores capítulos.
De manera formal el algoritmo, expresado en lenguaje natural, detalla los pasos a seguir en una lista ordenada.
Por ejemplo, el algoritmo para cacular el perímetro y área, de un círculo de radio "r", puede ser expresado de la siguiente forma:
Casi siempre, los algoritmos son codificadas (programados) en funciones.
Las funciones son bloques de código independientes, se caracterizan porque reciben los datos que requieren, llevan a cabo las operaciones necesarias y devuelven él o los resultados requeridos.
Al ser bloques de código independientes, permiten resolver problemas complejos dividiéndolos en problemas más simples. Cada uno de esos problemas más simples (cada una de las funciones) se conoce como módulo. Este principio (el principio de divide y vencerás): dividir un problema complejo en problemas más simples, es lo que se conoce como programación modular.
En JavaScript las funciones pueden ser creadas en al menos 3 formas: funciones estándar, funciones anónimas y funciones flechas.
Las funciones estándar tienen la siguiente sintaxis:
El nombre_de_la función, sigue las reglas generales para nombrar identificadores en JavaScript.
Las instrucciones JavaScript son instrucciones consecutivas separadas con puntos y comas (;). Una función puede tener entre 1 y cientos de instrucciones.
La instrucción de retorno: return, termina la ejecución de la función y devuelve el valor o el resultado de la expresión. Esta instrucción es opcional y puede estar ubicada en cualquier parte de la función, aunque generalmente se ubica al final de la misma. Si en la función no se escribe la instrucción return, la función termina cuando se ejecuta su última instrucción y no devuelve ningún valor (el resultado es indefinido: undefined).
La lista_de_parámetros son los nombres de las variables (separadas con comas) en las que la función recibe los datos que necesita. Los parámetros, así como las variables declaradas dentro de la función, son locales y temporales, es decir que sólo son válidas dentro de la función y dejan de existir (son destruídas) cuando la función termina.
Por ejemplo, en la siguiente función, se programa el algoritmo elaborado en la anterior sección (para calcular el perímetro y área de un círculo):
Donde el resultado se devuelve en un arreglo (un array) porque no es un resultado, sino dos: el perímetro y el área.
Una vez creada una función, se la llama (se la emplea), simplemente escribiendo su nombre, en este caso: paCirculo y mandando dentro los paréntesis él o los datos requeridos (los argumentos), en este caso sólo uno: el radio.
Por ejemplo, en las siguientes instrucciones, se llama a la función paCirculo, para calcula el perímetro y área de un círculo de radio 5 y otro de radio 2:
Las funciones pueden ser creadas también como funciones expresión, también denominadas funciones anónimas de acuerdo a la siguiente sintaxis:
Donde nombre_de_la_función es la variable en la cual se guarda la función. Este tipo de funciones se denominan funciones expresión, porque se crean en forma de una expresión de asignación y se denominan funciones anónimas porque la función en sí no tiene nombre, por eso es necesario guardarla en una variable. Como de costumbre, se emplea "var" cuando se trabaja en la calculadora (para poder hacer correcciones en caso de error) y const (más frecuentemente que let) cuando se crean variables al interior de una función o librería (se emplea let cuando el valor asignado a la variable debe, o puede, cambiar dentro del programa).
El cuerpo de la función (el código que en realidad resuelve el problema) es el mismo que en la función estándar y en realidad es el mismo en todas las formas en las que se puede crear una función, sólo cambia la forma en la que se crea la función (la forma en que se declara la función), así la función "paCirculo", programada como una función anónima, es:
Que se emplea igual que una función estándar, así el perímetro y área de un círculo con un radio igual a 3, es:
Finalmente, las funciones pueden ser creadas también como funciones flecha, de acuerdo a la siguiente sintaxis:
Que es una forma parecida a las funciones anónimas, excepto que no se emplea la palabra function y después de los parámetros se escribe una flecha: =>. Esta es la forma más práctica cuando la función consta de una sola expresión, pues en esos casos no se requieren ni las llaves, ni la instrucción de retorno (return). Además, si la función recibe solo un parámetro, no se requieren tampoco los paréntesis.
La función "paCirculo", programada como una función flecha, es:
Por supuesto, se emplea igual que cualquier función. Así, el perímetro y área de un círculo con un radio igual a 7, es:
Como se dijo, si la función flecha sólo consta de una expresión, no requiere ni llaves ni la instrucción return. Si además, recibe un sólo parámetro, tampoco requiere los paréntesis. Así para programar la siguiente expresión:
\[ \sqrt[3] {{y+ 4! + y^{3.2}} \over {\text{e}^y + \cos(y)} } \] |
Se escribe:
Por esta razón, las funciones flecha son la elección lógica cuando la función consta de una sola expresión.
Sin importar como haya sido creada una función, siempre se la emplea (se la llama) de la misma forma, es decir escribiendo el nombre de la función ("fy") y, entre paréntesis, el valor requerido ("y"). Así el valor de la expresión, para y igual a 3.1, se calcula con:
El pseudocódigo emplea instrucciones similares a las de un lenguaje de programación para expresar el algoritmo.
Debido a que no existe un estándar oficial, cada programador escribe el pseudocódigo de forma diferente.
Por ejemplo, el algoritmo para caclular el perímetro y área de un círculo, en forma de pseudocódigo, puede ser escrito de la siguiente forma:
Dado que es similar a un lenguaje de programación, el pseudocódigo no siempre resulta claro. Además, como cada programador emplea su propio pseudocódigo, los algoritmos son claros únicamente para el que los elabora. Por esta razón, en la asignatura no se empleará este tipo de algoritmos.
El código documentado, al igual que el lenguaje natural, ya ha sido empleado en los anteriores capítulos para explicar la lógica seguida en la elaboración del programa.
Es una forma muy práctica, porque permite comprender la lógica y al mismo tiempo analizar el código que lo implementa. De esa forma es más sencillo corregir errores así como modificar y optimizar los programas. Por esta razón, actualmente, es una de las formas más empleadas en el desarrollo de software.
El algoritmo para calcular el perímetro y área de un círculo, en forma de código documentado, es:
Los de diagramas de flujo, son una de las formas favoritas para expresar algoritmos.
Al ser una forma gráfica, es más expresiva, sin embargo, sólo son de utilidad para elaborar algoritmos simples. Los algoritmos complejos tienen tantos elementos gráficos que resultan difíciles de comprender, con lo que el diagrama pierde su propósito.
En la asignatura, los diagramas de flujo son elaborados con la ayuda de la librería GoJS.
En los diagramas de flujo se emplearán las formas geométricas estándar. Así, el inicio y el fin se representan con nodos rectangulares de lados redondeados (píldoras):
La entrada de datos y la salida de resultados, se representan, respectivamente, con un paralelogramo y un rectángulo de fondo ondulado:
Para las condiciones (preguntas) se emplea un rombo:
El símbolo para las instrucciones (procesos, operaciones, comandos) es un rectángulo:
Los módulos (subrutinas) se representan con un rectángulo, con dos líneas verticales adicionales a ambos lados:
Aunque no es propiamente un estándar (pero sí una forma ampliamente utilizada en la práctica) para representar la estructura for, se emplea un rectángulo con sus lados en forma de vértices:
Para conectar dos puntos separados del diagrama, se emplean círculos:
Por ejemplo, el siguiente diagrama, corresponde al algoritmo para calcular (y devolver) el perímetro y área de un círculo de radio "r":
Como se muestra en este ejemplo, para escribir expresiones matemáticas en los diagramas, se emplean los operadores y funciones de JavaScript, pero, las funciones y constantes matemáticas son escritas sin Math., para que sean más universales y más cortas.
Una vez elaborado el diagrama de flujo, la codificación (el escribir el programa propiamente) es mayormente un proceso mecánico, pues lógica, que es la secuencia de acciones para resolver el problema, ya está definida en el diagrama de flujo.
Así, el código correspondiente a este diagrama, es:
Los diagramas de flujo, en GoJS, se crean como se muestra en el siguiente video, donde se elabora el diagrama de flujo para sumar dos números:
Además, cualquier acción puede ser deshecha (undo) con el atajo estándar Ctrl+Z y rehecha (redo) con el atajo estándar Ctrl+Y.
En los teléfonos y tabletas se trabaja de la misma forma, sólo que varias de las opciones, como: copiar (Copy), pegar (Paste), cortar (Cut), eliminar (Delete), deshacer (Undo), rehacer (Redo), etc., están disponibles a través del menú contextual que aparece cuando se hace una pulsación larga en uno de los elementos de la gráfica. Además, en los ejercicios, para evaluar la gráfica (con Shift+Enter) debe estar seleccionado un texto (cualquier texto de la gráfica), para que aparezca el teclado y sea posible pulsar las teclas Shift+Enter.
En este ejemplo se calcula y devuelve, el área de un triángulo de lados "a", "b" y "c", empleando la siguiente fórmula matemática:
\[ \begin{aligned} \text{Area} &= \sqrt{s(s-a)(s-b)(s-c)}\\[0.3cm] donde : &\phantom{=}\\ s &= \dfrac{a+b+c}{2} \end{aligned} \] |
El algoritmo, en forma de diagrama de flujo, es:
Note que, aún en un problema tan simple como el presente, existe una secuencia lógica que es necesario respetar: primero se debe calcular "s" y luego el área, porque en el cálculo del área se emplea "s", es decir que no es posible calcular el área sin antes haber calculado "s", al igual que no es posible subir a un tercer piso, sin haber subido primero al segundo.
El código respectivo, implementado como una función estándar, es:
Donde, la variable local s, ha sido declarada como constante, porque su valor (una vez calculado) no cambia. Llamando a la función con algunos valores de prueba, se obtiene:
En este ejemplo se calcula y devuelve, la altura que deberá tener un cilindro de diámetro "d", para almacenar un volumen "V" de agua.
La altura se del cilindro se calcula con:
\[h = \dfrac{4 \cdot V}{\pi \cdot d^2}\] |
El algoritmo en forma de diagrama de flujo, es:
Siendo el código respectivo, programado como una función anónima:
Llamando a la función (con valores en unidades compatibles) para calcular la altura del cilindro en los siguientes casos: diámetro = 4 pies, volumen = 55 pies3; diámetro = 48 pulgadas, volumen = 90000 pulgadas3; diámetro = 1 m, volumen = 1,5 m3, se obtiene:
Donde el primer resultado está en pies (porque los datos están en pies), el segundo en pulgadas y el tercero en metros.