La programación imperativa es el enfoque de programación más antiguo y más desarrollado. Surgió con las primeras computadoras a mediados de la década de 1940, cuando John von Neumann y otras personas científicas plantearon la de idea de una máquina que pudiera almacenar tanto los datos como los programas en su memoria principal dando nacimiento a las computadoras de programa almacenado, que posteriormente conoceríamos como Arquitectura de Von Neumann.
<aside> 🤓 Hasta ese momento, las computadoras se programaban usando cables de interconexión para enrutar datos y para controlar señales entre varias unidades funcionales. Un ejemplo de este tipo de máquinas es la famosa ENIAC (Electronic Numerical Integrator And Computer), programada por seis mujeres que no han recibido la fama suficiente por su invaluable labor: Betty Snyder Holberton, Jean Jennings Bartik, Kathleen McNulty Mauchly Antonelli, Marlyn Wescoff Meltzer, Ruth Lichterman Teitelbaum y Frances Bilas Spence.
</aside>
La Arquitectura de Von Neumann es la base del paradigma de programación imperativa, pues el núcleo de esta arquitectura es la idea de asignación, es decir, cambiar el valor de una posición de memoria y destruir su valor anterior. Todos los lenguajes imperativos incluyen la asignación como elemento central y todas las operaciones se basan en acceder a la memoria y manipular sus valores a través del uso de la idea de asignación.
Las declaraciones de variables asignan nombres a las posiciones de memoria y asocian tipos a los valores almacenados.
Luego, las expresiones se interpretan recuperando los valores actuales de las variables nombradas de sus respectivas posiciones de memoria y calculando un resultado a partir de esos valores.
Todo eso sucede, normalmente, en una ejecución secuencial de las instrucciones que aparecen en la memoria de la máquina donde está almacenado el programa.
Luego, dada la necesidad, surgieron las sentencias condicionales que permitieron interrumpir este flujo normal de ejecución y abrieron paso a la bifurcación del código.
<aside> 🤓 Debido al uso extensivo de condicionales, los primeros programas imperativos se modelaban a menudo con la ayuda de diagramas de flujo.
</aside>
En sus primeras versiones, los lenguajes imperativos estaban formados por comandos que no eran más que simples abstracciones de las instrucciones de asignación y las sentencias condicionales y las sentencias de bifurcación de las máquinas von Neumann. De esta forma, estos primeros lenguajes imperativos que podían trabajar con asignaciones, variables y valores enteros, realizar operaciones aritméticas básicas, secuenciación de sentencias basada en memoria, condicionales y sentencias de bifurcación cumplían con la regla de ser Turing completos.
Estas instrucciones combinadas hacían que los lenguajes fueran Turing completos. Un lenguaje es Turing completo si proporciona una base eficaz para implementar cualquier algoritmo que pueda diseñarse, es decir tiene el poder equivalente a la máquina de Turing universal.
<aside> 🤓 Una máquina que pueda actuar como una máquina universal de Turing puede hacer cualquier cálculo que cualquier otra computadora es capaz de hacer.
</aside>
Entonces, a modo de resumen, podemos decir que un lenguaje de programación imperativo es aquel que es Turing completo y que también soporta ciertas características comunes que han surgido con la evolución del paradigma de programación imperativa y que ya conocemos porque tenemos experiencia en el arte de programar: