Aprender a programar desde cero
Artículo publicado en 2021 y revisado en 2023
Una vez definido qué hace un programador (su rol y herramientas) y qué conviene tener en cuenta para garantizar un mínimo de ciberseguridad, he recopilado los conceptos que parece esencial entender antes de decantarse por un lenguaje y especialización concretos.
Introducción a la programación: ordenador digital
Hay dos métodos para captar el mundo con aparatos: analógico y digital. Los sistemas electrónicos digitales de ciertos dispositivos des/recomponen los textos, imágenes y audio de la realidad en la unidad mínima de información.
Cada dato será un conjunto de unidades determinado, finito. Se decidió que cada unidad solo pudiese tener dos estados lógicos y que se representase con el ya conocido sistema binario (base 2).
Esto permitió más estabilidad frente a mayor número de estados y que para las operaciones informáticas se pudiera aplicar el álgebra booleana. Era un sistema matemático basado en la lógica de verdadero o falso desarrollado décadas antes del nacimiento de los ordenadores.
En el sistema de numeración binario, cada unidad es un dígito binario o binary digit, de ahí, bit. Sus niveles posibles se marcan con un 1 o 0. Estas dos cifras deben entenderse de un modo abstracto (sí/no, encendido/apagado), no como números.
Los bits pueden almacenarse de varias maneras: como presencia/ausencia de tensión eléctrica (es el caso de una memoria RAM, por ejemplo), estado de magnetización norte/sur (disco duro) o valles/picos de luz (DVD).
Cómo se procesa la información digital
Muy en resumen, gracias a muchísimas operaciones de puertas lógicas (NOT, AND, OR, XOR) que sus microcomponentes hacen posibles, las unidades ejecutivas de una computadora (CPU, ALU) leen las instrucciones de los programas (y calculan) desde/hacia las memorias (volátil, persistente) y otros dispositivos conectados.
Cada modelo de máquina tiene unos circuitos integrados diferentes, por lo que cataloga la información en su particular lenguaje binario (código máquina). Es decir, por ejemplo, mientras en mi ordenador la palabra “leer” empezaría a guardarse como 10101010, en una tableta se registraría como 00010100...
Entonces, ¿cómo puede asegurarse el procesador de textos de que ambos dispositivos perciben la misma palabra? Pues para que máquinas distintas se refieran a lo mismo, se utilizan códigos estándar (intermedios) con los que traducir la información que entra/sale; es el caso de ASCII o UNICODE en textos.
Al final, las computadoras muestran y guardan información que los usuarios no percibiremos como bits, sino como colores o sonidos.
Qué hace un programa informático
Los dispositivos digitales pueden resolver ciertos problemas lógicos. Convierten unos datos entrada (por teclado, ratón…) en unos datos de salida (pantalla, altavoces…). ¿Qué deseamos que cambie en la realidad y qué manera pensamos que es la más conveniente para conseguirlo?
Tanto en programación como fuera de ella, un algoritmo es una serie de pasos no ambiguos para lograr un determinado resultado (siempre). Generalmente hay fórmulas y datos entrantes variables implicados.
Hay que diseñar y verificar cada algoritmo concienzudamente antes de escribir código. Su estructura lógica se puede representar con diagramas de flujo y/o en lo que se conoce como pseudocódigo.
Ya se sabe de muchos algoritmos que son útiles para unas cosas más que para otras. Conocerlos puede ayudar a encontrar fórmulas más eficientes y ahorrar tiempo.
Un conjunto de algoritmos
Un programa consiste en elegir ciertas órdenes (algoritmos) y escribirlas en lenguajes de alto nivel (diferentes al de la máquina), pues son más cercanos a las lenguas naturales. Tal texto es el código fuente y también se debe validar que da los resultados esperados.
Los programas simples o scripts los puede ejecutar un intérprete o una consola interactiva en muy diversas máquinas. Por su parte, los programas complejos requieren de un compilador (y a menudo de un enlazador) para que tras una o varias traducciones se dé lugar a un código ejecutable que algunas máquinas podrán cargar y comprender (determinada arquitectura y sistema operativo). Más o menos.
Tengamos en cuenta que los programas pueden ser autocontenidos o no (en tal caso se requerirá que haya ya ciertas cosas instaladas en ese sistema).
Las interfaces
Una interfaz es el puente que permite la comunicación entre dos partes que de otro modo no se entenderían. Existen por lo menos tres tipos de interfaces:
- Físicas: conectan dos dispositivos (como un cable).
- De usuario (user interface > UI): el usuario y la máquina pueden intercambiar datos tanto por medio de interfaces de hardware (teclado, ratón) como de software (programa). La información puede tomar varias formas:
- Línea de comandos (CLI): solo texto. Esta se mostrará al abrir un programa emulador de terminal. A través de esta interfaz se pueden realizar muchas acciones que son posibles mediante una interfaz gráfica, pero también otras más a mayores. En programación se utiliza mucho la terminal porque además son la manera por defecto para comunicarse con ciertos programas (como los de control de versiones), los lenguajes interpretados, los servidores o los gestores de bases de datos.
- Gráfica (GUI): texto, imágenes, gráficos. Son las típicas ventanas con menús de los sistemas operativos y mayoría de programas (incluyendo los navegadores). Su diseño va unido a la experiencia de usuario (UX < user experience).
- Voz o Natural (NUI): usuario y aparato interactúan con medios humanos como el tacto o el habla (caso de los asistentes de voz).
- Interfaces lógicas: interaccionan dos sistemas independientes como los programas. Es el caso de los estándares que gestionan los programadores tal que las API (Application Programming Interface). Estos intermediarios para el intercambio de datos entre programas pueden ser públicos o no.
En el caso de desarrollo de aplicaciones para un sistema operativo específico, las API se pueden acompañar de otras herramientas (documentación, código insertable), lo cual conforma un SDK (Software Development Kit).
La red y los servidores
Los dispositivos digitales pueden conectarse unos a otros a través de los adaptadores de red, las redes, los protocolos y otros elementos.
Aquel aparato que pone datos a disposición de la red es un servidor. Como computadora que es, requerirá ciertos programas propios. Por otro lado, los programadores no solo escriben código que estará integrado en el equipo, sino que los programas se pueden descargar de la red y también se programan soluciones para los sitios web, para las progressive web apps o para compartir archivos.
Hay que distinguir entonces entre dos ordenadores: lo que se muestra en el lado del cliente o front-end (navegador del usuario) y la información interpretada en el lado del servidor o back-end (aquella enviada o disponible para peticiones). Bueno, en el sistema de nodos P2P la cosa cambia un poco.
Front-end <=> (API <=>) Back-end <=> (ORM <=>) Bases de datos
Fundamentos de la programación informática
Al crear un programa, se elige un paradigma de programación, probablemente un patrón de diseño y seguro que un lenguaje (este puede estar muy determinado por el paradigma). En proyectos no pequeños, el programador no es quien decide, sino quien elabora lo que otros han marcado, esto es, escribe una parte de código en cierto lenguaje que haga tal cosa.
Paradigmas de programación
Un paradigma de programación es un estilo documentado de organizar el código y sus elementos. Se distingue entre al menos estas grandes vías:
- Imperativa: en esta programación se indica detalladamente cómo se debe hacer cada tarea. Una de sus subclases es la programación estructurada, por la que se empieza cuando se aprende (secuencial).
- Declarativa: una vez programado el objetivo, la máquina lo alcanza solo con razonamiento lógico.
- Orientada a objetos: se describe más adelante.
Hay otros tipos o subtipos (funcional, procedural, modular, reactiva), pero habiendo encontrado afirmaciones contradictorias no me ha quedado claro aún como se relacionan con las anteriores.
Patrones de diseño de software
Para ciertos problemas ya existen soluciones muy testeadas y estandarizadas que cualquiera puede aplicar como base y con las que el sector está familiarizado: los patrones de diseño de software (design patterns). Suele distinguirse entre tres o cuatro categorías según su propósito:
- Creacionales: crear clases u objetos.
- Estructurales: su composición.
- De comportamiento: interacción entre ellos.
En cada categoría hay varios enfoques posibles (+20), con nombres bastante descriptivos de lo que proponen. Así, en el patrón creacional Prototype se clona a partir de una plantilla.
>> Lista de vídeos sobre los principales patrones de diseño
Por cierto, de forma equivalente, tienen lugar también antipatrones, es decir, soluciones de diseño que conviene evitar.
Los lenguajes de programación de alto nivel
Los programas han de escribirse siguiendo ciertas reglas de sintaxis de cada lenguaje e indicaciones de semántica. Al ejecutar un algoritmo, la unidad de control intentará seguir las instrucciones con los datos insertados o disponibles. Para que la máquina reconozca qué hacer con los datos, estos tienen que definirse.
No todos los vocablos valen durante la explicación (algoritmo) que se le traducirá a la máquina. Cada lenguaje de programación tiene unas palabras reservadas con un cometido específico si no están entre comillas. Algunas son comunes a la mayoría, otras son particulares de cada lenguaje. Por ejemplo, if suele abrir condicionales en varios lenguajes, mientras que los resultados aparecen en consola mediante print en el lenguaje Python y console.log en Javascript.
El elemento mínimo del código fuente que puede hacer algo es la sentencia. Estas son declarativas (describen propiedades) o bien conllevan una acción por parte de la máquina (obtener/proporcionar datos, calcular).
El código fuente acostumbra a estar acompañado de comentarios. Tales anotaciones (introducidas por ciertos símbolos como / o #) aclaran el código para el propio autor en el futuro u otros programadores. Son útiles como guía, al trabajar en equipo, para revisión o mantenimiento.
Hoy los programas en los que se edita código tienen una paleta de colores que ayuda a identificar cuándo se están rompiendo las normas del lenguaje (destaca errores que no se verían hasta repasar, compilar o ejecutar una prueba).
Identificar los datos
Los datos pueden utilizarse de manera literal o estar representados simbólicamente por un alias. Este identificador o contenedor asigna un nombre determinado a un campo de datos desde ese momento y en ese bloque. Por ejemplo, enseñarle a la máquina que title se referirá a un título de libro. Tal nomenclatura tendrá un sitio reservado en la memoria.
Cuando un identificador se relaciona con un dato en concreto se llama declaración. Dependiendo del lenguaje, esta asociación simplemente tiene lugar o hay una palabra para marcarla.
Estas nomenclaturas han de tener consistencia (todas coincidir con el mismo modelo). Existen estándares de notación y guías de estilo de cada lenguaje. Así, seguirían distintas convenciones de nombres los identificadores book-author, book_author, BookAuthor…
Una nomenclatura será una constante (el mismo valor siempre que se ejecute) o bien una variable, si puede ser diferente cada vez. Por ejemplo, en un buscador o catálogo de biblioteca, first_loan podría ser una constante con 15 días como dato permanente para el primer préstamo y title una variable para la que se pueden declarar Diccionario de uso del español o bien La Ilíada como dato posible entre otros muchos.
Tipos de datos
Los lenguajes informáticos aceptan diversos tipos de datos para las variables: números (natural, entero, decimal), cadena alfanumérica (texto), booleano (verdadero/falso)…
Tales tipologías se usarán para diversas necesidades. En un videojuego, por ejemplo, con los números se especifica la velocidad de un elemento, una cadena de texto corresponde a un diálogo que se muestra en pantalla y un booleano marca que un personaje llegó a determinado punto (o todavía no).
En el caso concreto del texto, a menudo es necesario comprobar datos muy específicos en cuanto a su composición y ubicación. Para ello es útil buscar con expresiones regulares (regex > regular expressions).
Tipado
Como ejemplifica este gráfico, algunos lenguajes deducen el tipo de dato sin que haya que declararlo (son por tanto de tipado débil o no tipado), otros requieren que se definan (tipado fuerte). Una mayor definición conlleva un uso de los recursos más adecuado a lo que se necesita, imposibilidad de hacer una operación entre distintos tipos y, por tanto, menos errores y más escalabilidad, aunque requiere más concreción.
Por otro lado, con respecto a cuándo se comprueba la relación entre tipos de datos, ciertos lenguajes verifican durante la compilación (tipado estático) y otros chequean la tipificación en la ejecución (tipado dinámico).
Gestionar los datos
Con el fin de indicarle a la máquina qué ir haciendo con los datos para dar un resultado u otro, hay una serie de operadores aritméticos, relacionales y lógicos (%, ==, !=, &&, ||, ++) con determinada jerarquía o precedencia.
Con estos se pueden construir las expresiones lógicas que componen las estructuras de control (condicionales, switches, bucles/ciclos for, while y do-while). De este modo se definen los posibles caminos que puede tomar una instrucción, para que la máquina decida por sí misma: secuencia, alternativa, iterar. Parece conveniente ahondar en el concepto de recursividad y poner cuidado en su aplicación.
Reutilizar el código más usado
Una subrutina/función (retorna valor) o un procedimiento (no retorna) es una sección independiente que opera por su cuenta y por tanto su código se puede reutilizar apelando (llamando) brevemente a ese bloque predefinido. Además de acelerar el tiempo de escritura, en caso de error en esa parte del código solo habría que corregir una vez. Sus variables de entrada se conocen como parámetros y los datos concretos de estos se denominan argumentos.
Las bibliotecas o librerías de cada lenguaje ya incluyen funciones para las necesidades más habituales al crear programas y conviene usarlas en lo posible por su eficiencia (pero solo si cuadran, claro).
Un framework es un conjunto de librerías (funciones) para desplegar muchas funcionalidades que puede tener un solo proyecto en un determinado lenguaje de programación.
Es decir, para programar en determinado lenguaje cierto proyecto, en vez de crear todo de cero, quizá ya existe un framework o varios que recopilan las funciones que más suele necesitar un programa o sitio así, como una plantilla o animaciones. De ahí se pueden utilizar las que más convenga e incluso combinar de varios frameworks si son compatibles.
Qué son los objetos en programación
Concretamente los paradigmas de programación orientada a objetos (POO) o el de prototipos trabajan con ciertos conceptos. De forma parecida a como funcionan los tipos de datos y los identificadores, existen las clases y los objetos. Como ejemplo, podríamos decir que serían respectivamente cualquier usuario de biblioteca (clase) y un usuario determinado (objeto).
La clase es un conjunto de atributos (características constantes y variables de un usuario de biblioteca como name, surname, patron_nr, borrowed) y métodos (comportamientos/funcionalidades posibles como put_on_hold, borrow, renew). Un objeto será el resultado cada vez que una clase tome forma (instanciación), es decir, cada usuario en ese ejemplo.
Los objetos pueden cambiar sus estados variables ejecutando un método, así como comunicarse con otros objetos recibiendo mensajes con argumentos. De este modo, si una persona toma prestado (método borrow) un nuevo libro, su lista de libros prestados (atributo borrowed) se incrementará en 1.
Acceder a los datos
Al ejecutarse, un programa maneja datos que tienen que almacenarse en algún sitio y marcar su relación de alguna manera.
Los datos pueden estar guardados de manera persistente en un archivo o bien ser leídos por la memoria RAM. La eficiencia es primordial.
Es preferible que los datos se guarden en tablas de archivos especiales o bases de datos en casos con considerable volumen o necesidad de actualización o seguridad. Para crear una base de datos se usa un sistema de gestión de bases de datos (DBMS). Hay lenguajes propios para crear, leer, actualizar o eliminar los datos en estos sistemas.
Estructuras de datos
Además de asignar un dato individual a una variable, un único identificador puede agrupar varios datos en la memoria RAM. Existen varias estructuras de datos, cuyos tipos principales podemos resumir en los siguientes:
- Vectores y matrices: En los vectores (arrays) su primer elemento tiene la posición 0 en esa hipotética fila unidimensional. Si un array tiene dos dimensiones, se trata propiamente de una matriz.
- Lista enlazada de nodos
- Tablas de hash
- Pilas (LIFO) o stack
- Colas (FIFO) o queue
- Grafos (como los árboles)
A cada proyecto le convendrá más una estructura de datos u otra según sus peculiaridades y necesidades (como la rapidez de consulta).
Esquema resumen
Entonces estas son las fases cada vez que se crea un programa, aplicación o sitio web:
- Identificar el problema en la realidad.
- Trasladar la solución a algoritmo en palabras habituales, diagrama de flujo o seudocódigo.
- Decidir arquitectura y paradigma, lenguaje y quizá ciertos patrones de diseño.
- Usar framework o librerías concretas para no tener que escribir ciertas funciones.
He hablado más de las herramientas en el post enlazado al principio de todo. Lo esencial es no invertir tiempo en cosas que no hace falta. Se puede preguntar. Programar bien consiste en saber qué usar, cuándo y de la manera más eficiente.
Información complementaria para programar
Asimismo, parece beneficioso adquirir nociones básicas sobre criptografía (para des/encriptar datos), accesibilidad (tener en cuenta a todo tipo de usuarios), responsividad, microservicios, máquinas virtuales (su utilidad para probar programas en otros sistemas) e incluso estadística.
Para estas y otras preguntas (derechos de autor, scraping...), quizá algún día me anime a buscar información.
En cualquier caso, los interesados en este campo debemos comprender los fundamentos de la programación. Si nos sigue llamando, pues a practicar la creación de algoritmos y probar a programarlos en un lenguaje del entorno más afín.