sábado, 19 de febrero de 2011

Lección 2: Primeras Frases

Es hora de que nos internemos ya en la codificación, y vayamos acostumbrándonos a ver y escribir código. Al igual que a andar se aprende andando, a programar se aprende programando. En esta lección no haremos ningún programa funcional, pero iremos viendo retazos de código que luego uniremos en las siguientes lecciones para hacer un programa.

El lenguaje de programación con el que vamos a empezar será C++, un lenguaje muy extendido y ampliamente conocido, del que se puede encontrar mucha información en Internet lo cual os solucionará muchos problemas. De momento no voy a entrar en las características de este lenguaje, excepto una, que es importante para empezar, y es que utiliza los tipos, de los que ya hablamos la anterior lección.

El sistema para el que vamos a programar es un ordenador tipo PC (Windows o Linux) o Mac, lo que se conoce como aplicaciones de escritorio, que no son aplicaciones web. Los programadores seremos nosotros.

A continuación vamos a ir repasando los elementos de los que se hablaron en la lección anterior.

Hemos dicho que C++ utiliza los tipos, y esto quiere decir que cuando creamos una variable tenemos que definir qué se va a almacenar en ella, y si intentamos almacenar otra cosa, el compilador nos lo va a impedir.
Los tipos básicos que de momento nos van a interesar de C++ son:
  • Los números enteros. En una variable de tipo entero se podrá almacenar cualquier número entero, que son números positivos o negativos sin parte decimal: -2346, -4, 0, 1, 46, 3389002...
  • Los números reales. En una variable de tipo real se podrá almacenar cualquier número, positivo o negativo, con parte decimal o sin ella (sí que tendrá pero no nos la representará): -34.17, -2.0, 0.14, 1.8989, 42456.12342...
  • Los caracteres. En una variable de tipo caracter se podrá almacenar cualquier caracter, que viene a ser letras, números, símbolos corrientes o símbolos especiales de impresión: a, X, 1, ª, @, fin_de_párrafo ¶...
  • Los booleanos. En una variable de tipo booleano se podrá almacenar true o false, lo que indicará que la variable es verdadera o falsa. Una aplicación de esto lo veremos en esta misma lección, y aunque la idea al principio puede ser confusa, la práctica nos dará el entendimiento.
Hay más tipos en C++ que iremos viendo en las siguientes lecciones. Pero conociendo éstos, ya podemos ver cómo se escribiría la declaración de una variable, que viene a ser crear la variable para luego poder usarla o, en un sentido más profundo, el decirle al sistema que nos reserve un hueco en su memoria (la RAM, por ejemplo), para que nosotros guardemos ahí información de un tipo correspondiente. En C++ se pone primero el tipo que queremos que tenga la variable y después el nombre, y si queremos que tenga un valor inicial, añadimos un símbolo igual (=) y después el valor que deseamos. Por último se pone punto y coma (;) ya que es una instrucción y todas la sinstrucciones en C++ terminan en punto y coma. Hay que tener en cuenta que veremos cosas que no se consideran instrucciones, como los bloques de control.

Veamos unos ejemplos de declaraciones. Si quisieramos declarar una variable entera para almacenar el número de piedras que tenemos, sería algo así:

int piedras;

Si además sabemos que empezamos con 12, la declaración sería:

int piedras = 12;

Los nombres de las variables los construiremos utilizando combinaciones de únicamente letras minúsculas (aunque se puede con mayúsculas también, lo haremos como costumbre), números y el caracter '_'.
Si queremos declarar un variable de tipo caracter, para almacenar los caracteres que nos entran por el teclado, un ejemplo sería:

char tecla_pulsada;

Pero si queremos poner una variable para un programa que busque un caracter en un texto, pondremos un valor inicial y sería:

char busqueda = '@';

Aquí podemos ver dos cosas interesantes. El lenguaje no admite tildes en en la declaración de variable (y básicamente en cualquier lugar que no sea como caracteres especiales). Y si queremos expresar un caracter en nuestro código, tenemos que rodearlo de la comilla (').

Para declarar una variable, por ejemplo, para la búsqueda de la que hablábamos antes, y que el programa pare cuando se haya encontrado, crearemos una variable booleana que empezará en falso y que pondremos a verdadera cuando encontremos el caracter. La declaración sería:

bool encontrado = false;

Si queremos definir una variable de tipo real será algo como:

float raiz; float pi = 3.1416;

En resumen habría que decir que los tipos son: enteros son int, los reales float, los caracteres char, y los booleanos bool.

El ejemplo de pi lo puse por una razón, y es que pi sabemos que es una constante, un número inalterable. En la lección pasada hablamos de las constantes, variables que no cambian. Son útiles ya que definen cosas inalterables, que se podrían poner directamente en el código sus valores, pero que al usar constantes, si queremos cambiar su valor sólo lo tendremos que cambiar en un sitio y no buscar todas sus apariciones en todo el código.
Por ejemplo yo he definido pi como 3.1416, pero si luego mi jefe me dice que necesitamos un decimal más de precisión, sólo tengo que ir a la declaración de pi y cambiarlo, mientras que si he puesto en vez de pi el número directamente en sus apariciones, tendré buscarlas todas y cambiarlo en todas. Además, si la defino como constante, me aseguro que si intento cambiarla por accidente mediante instrucciones del programa, el compilador no me lo permitirá.

Las constantes se declaran igual que las variables pero añadiendo delante la palabra const:

const float pi = 3.1416;
const bool verdad = true;
const char arroba = '@';

Cuando ponemos, en nuestro código un número, caracter o valor booleano directamente, como al darle un valor inicial a una variable o constante (por ejemplo en las últimas declaraciones 3.1416, true o '@') a esto se le llama constante literal. Estamos creando una constante de un solo uso y totalmente explícita, sin el intermediario de un nombre. Como hemos visto, las constantes literales de los enteros se crean poniendo simplemente el número, el de los reales igual, solo que si tiene decimal, usamos el punto (.) para separar parte real y parte decimal, los caracteres se muestran entre las comillas simples (') y los booleanos se ponen o true o false.

Hay una forma de declarar varias variables de un mismo tipo en una sola línea y es poniendo el tipo y de todas ella y luego los nombres separados por comas. Se puede asignar además un valor inicial a las que se desee:

int num1 = 0, num2 = 3, num3;

Esto declararía tres variables con nombre num1, num2 y num3 y le asignaría 0 y 3 como valores iniciales respectivamente a las dos primeras.

Veamos ahora algunos operadores del lenguaje.

La asignación nos permite darle valores a las variables. Ya lo hemos usado en los ejemplos anteriores. Se representa mediante el símbolo de igualdad (=) y a la izquierda se pone la variable a modificar, y a la derecha el nuevo valor. El nuevo valor no tiene por qué ser una constante literal, puede ser otra variable, una constante o cualquier otra expresión. El compilador comprobará si el tipo de la parte de la izquierda es el mismo que el de la expresión de la derecha. Si por ejemplo hacemos estas tres líneas de código:

int num1 = 2;
int num2 = 3;
num1 = num2;

Al final de la ejecución en num1 estará el valor 3 y el compilador no nos dará error ya que ambos son del mismo tipo. En este ejemplo había tres asignaciones, como se puede comprobar por los tres símbolos (=). En el siguiente ejemplo el compilador nos daría un error:

char caracter = 'a';
int num1;
num1 = caracter;

Estamos intentando meter una variable de tipo char en un de tipo int, y eso el compilador nos lo impedirá. En este caso parece claro que no se pueda pero, ¿qué pasa si intentamos meter un entero en un real (int en float)?

Veamos ahora otros operadores a parte de la asignación que nos permitirán modificar nuestras variables. Los operadores más comunes son:
  • Aritméticos. Manipulan o dos enteros o dos reales y devuelven o un entero o un real. Son: la suma (+), la resta (-), la multiplicación (*), la división (/), el módulo o resto (%). Hay uno que sólo recibe o un entero o un real y devuelve un entero o un real, que es el negativo (-).
  • Relacionales. Manipulan dos enteros o dos reales y devuelven un booleano. Son: menor que (<), mayor que (>), igual que (==), diferente que (!=), menor o igual que (<=) y mayor o igual que (>=).
  • Lógicos. Reciben uno o dos booleanos y devuelven un booleano. Son: negación (!), and (&&) y or (||). Si la negación recibe un true, devuelve un false, y si recibe un false, devuelve un false. El and devolverá true cuando ambos booleanos sean true, y falso si solo uno o ninguno son true. El or devolverá true si al menos hay alguno que sea true y devuelve false si ambos son false.

Veámoslos en acción:

int operando1 = 2, operando2 =3, resultado1, resultado2, resultado3, resultado4;
bool mayor, menor;

resultado1 = operando1 + operando2;
resultado2 = operando1 * operando2;
mayor = resultado1 > resultado2;
menor = !mayor;
resultado3 = operando1 * operando1 + operando2;
resultado4 = operando1 * (operando1 + operando2);

Analicemos ahora los resultados de este trozo de código. Al final de la ejecución de estas instrucciones, en resultado1 debería encontrarse un 5 (2 más 3), en resultado 2 un 6 (2 por tres 3), en mayor un false (ya que el 5 de resultado1 no es mayor que el 6 de resultado2), en menor un true (ya que el inverso de false es true), en resultado3 un 7 (ya que la multiplicación se realiza antes, al igual que en matemáticas) y en resultado4 un 10 (ya que si ponemos paréntesis, se realiza antes la suma de 2 más 3 y luego se multiplica esto por 2.

Es importante, pues, tener en cuenta la precedencia de cada operador, lo que significa conocer cuáles se ejecutan antes que otros. Esto no lo incluiré en la lección ya que de momento será algo intuitivo, aunque si se tiene curiosidad se pueden consultar las precedencias en cualquier manual del lenguaje.

Quiero destacar también el uso de los tipos en el ejemplo. Los operadores devolvían en toda ocasión los tipos que la variable a la que se lo asignábamos podía almacenar. Los resultados de operadores relacionales o lógicos fueron asignados a variables booleanas, y los resultados de operaciones sobre enteros fueron asignadas a variables de tipo entero.

Con todo esto hemos cubierto ya un tercio de la base de la programación, nos faltarían las estructuras de control y las funciones, que veremos en la próxima lección.

Un saludo y para cualquier duda, escribid un comentario.

sábado, 18 de diciembre de 2010

Lección 1: Primeros Pasos

Lo primero de todo es entender qué es la programación. Vemos los programas y sabemos que los realizan los programadores, pero cuál es realmente su función, es algo muy distinto. Propondré para explicar esto un ejemplo sencillo que luego servirá para explicar los elementos que forman la programación.

Imaginemos a un niño que va caminando por la playa y al que le pedimos que meta las piedras blancas que encuentre en una bolsa blanca y las piedras negras en una bolsa negra. Pues esto sería "programar", indicar a un sistema una serie de instrucciones para operar sobre una serie de elementos. El conjunto de instrucciones se llama código. Nuestro código en este caso sería algo como:

Si la piedra es blanca, métela en la bolsa blanca.
Si la piedra es negra, métela en la bolsa negra.

Para si no quedan más piedras en la playa.

El nivel de detalle con el que vamos a tener que dar nuestras instrucciones será el ajustado por lo que se conoce como el lenguaje de programación, así como la gramática que utilizaremos para expresarnos y ser entendidos. En el ejemplo el lenguaje de programación sería el español ya que es el que ambas partes entendemos. En el caso de las máquinas utilizaremos un lenguaje que ellas entienden y que nosotros podemos aprender.

La gran mayoría pensará que esto es obvio, pero es importante tener los conceptos claros y nunca está de más repasar hasta lo más básico.

Veamos ahora qué elementos están interviniendo:
-el niño
-nosotros
-las piedras
-las instrucciones que le hemos dado

El niño en este caso sería el sistema, que recibirá nuestras instrucciones. El niño es suficientemente inteligente como para realizar las instrucciones que le vamos a pedir, y eso es importante, pero las instrucciones deben ser, y esto lo veremos más adelante, suficientemente inequívocas como para que sólo pueda haber una interpretación de ellas. Los sistemas, por lo general, son extremadamente tontos y sólo saben cumplir con las instrucciones más básicas, y esto, es en parte así, porque es la única manera de que no se confundan. Si yo le doy al niño dos bolsas idénticas y le pido que según vea piedras las meta en cada una de las bolsas diferenciándolas por color, tal vez no obtenga el resultado que yo espero al final de la tarea. El niño puede haberse encontrado con piedras rojas y las podría haber metido con las negras, cuando yo quería que no las cogiese. En este caso mis instrucciones no son claras, y el sistema las ha malinterpretado. Esto no debe suceder jamás y por eso hay que tener en cuenta que los sistemas sólo saben obedecer instrucciones inequívocas y simples.

Nosotros en este caso somos los programadores, somos los que damos instrucciones al sistema, y para ello debemos conocer el lenguaje con el que comunicarle las instrucciones y saber bien qué queremos que haga el sistema.

Las piedras son los objetos o elementos que manipulamos, transformamos o almacenamos, que en programación se conocen como variables. Al final querremos hacer algo con estas piedras, tal vez meterlas en un tarro, tal vez pintarlas, tal vez simplemente saber cuántas había... En los sistemas informáticos las variables son simplemente información. Serán cifras bancarias, palabras, imágenes o cualquier tipo de información que se nos pueda ocurrir y una máquina pueda almacenar o procesar.
La definición de piedra, la imagen genérica que engloba a todas las piedras dentro de la misma categoría y que nos hace reconocer una allá donde la vemos, sería el tipo. Los tipos se utilizan para definir qué clase de información se almacena en una variable. Los tipos más clásicos y básicos para las variables en la programación son: números (enteros o reales), letras (llamadas caracteres), palabras (llamadas cadenas de caracteres) y afirmaciones de verdad (llamados booleanos y cuyos valores son true (si es cierto) o false (si es falso)).
Hay bastantes lenguajes que no utilizan los tipos pero como hay otros muchos que sí, veo fundamental explicarlos, y es bueno conocerlos y acostumbrarse a ellos porque asienta mucho mejor unas buenas prácticas para la programación.
Si en el lugar que tenemos reservado para una piedra (la mano del niño o la bolsa) tratamos de introducir algo que no es una piedra, el niño protestará. Le hemos mandado recoger piedras y si se encuentra con una pala, no está en su reconocimiento asociativo el pasar una pala por una piedra, y si intentamos forzarle a hacerlo nos dirá que eso no es una piedra y que le hemos mandado coger piedras. Para eso sirven los tipos, para avisarnos de usos indebidos de los datos.

Por último tenemos las instrucciones, que deben estar en un lenguaje común al sistema y al programador y ordenadas adecuadamente a la secuencia de eventos que queremos que ocurra. Las instrucciones son las que harán que los datos de las variables cambien y se transformen y al final obtengamos el resultado deseado.
Es importante resaltar el hecho de que deben estar convenientemente ordenadas. Un orden erróneo de las instrucciones da lugar a resultados erróneos. Imaginemos que ahora al niño le mandamos otra tarea, que según le vayamos dando tarros llenos de piedras, las cuente, las multiplique por 5, que es la cantidad por la que le vamos a vender, y luego le sume 20 por el precio del tarro. Si nosotros le decimos:

Cuenta las piedras.
Multiplica por 5.
Suma 20.


Obtendremos los valores deseados, pero si alteramos las instrucciones en otro orden:

Cuenta las piedras.
Suma 20.
Multiplica por 5.


No obtendremos el valor deseado. Por ejemplo, si le damos un tarro con diez piedras, en el primero caso tendremos un resultado de 70, mientras que en el segundo de 150. El orden es realmente importante.

También es importante darse cuenta que lo que queremos hacer nosotros es un programa válido para que le podamos pasar infinitos tarros, y no uno solo. Si quisiéramos darle un único tarro de 15 piedras, lo podemos calcular de cabeza. Pero programamos para automatizar procesos que se van a repetir en el tiempo y cuyos datos de entrada no van a ser siempre los mismos. Si siempre llegasen tarros de 15 piedras, no tendría sentido hacer un programa.

En este ejemplo el número de piedras, el 20 y el 5 serían variables, aunque las dos últimas serían constantes, y no cambiarían nunca. Habría una variable implícita que es el conteo de las piedras, ya que las piedras son de tipo piedra, pero el conteo es de tipo entero (número entero), son variables distintas. El 20 y el 5 serían también de tipo entero.

Las instrucciones más comunes implican operaciones aritméticas sobre números (sumar, restar, multiplicar y dividir), operaciones de comparación (mayor que, menor que, igual que), asignación de valores (ahora esta variable vale esto), operaciones lógicas (que ya veremos más adelante) y bloques de control (del tipo "si se cumple esto haz esto" o "mientras pase esto realiza estas acciones").

Son realmente dos cosas distintas los operadores y los bloques de control, pero al fin y al cabo son el alma de las instrucciones y por eso las he explicado juntas, pero en la siguiente lección las explicaré más a fondo por separado.

Por último voy a explicar dos conceptos sobre las herramientas que tiene que utilizar un programador, el editor de textos y el compilador.

El editor de textos es sencillo, es simplemente una herramienta informática donde escribir textos, como puede ser el bloc de notas de Windows o el programa para documentos de OpenOffice. Es donde escribiremos las isntrucciones de nuestros programas.

El compilador es un programa que traduce nuestro código al lenguaje que entienden las máquinas, el de los unos y los ceros. El compilador entederá un lenguaje que se parece más al que utilizamos nosotros y lo traducirá al que entienden las máquinas. Programar en el lenguaje que utilizan las máquinas es engorroso, lento y poco abstracto y aunque a veces se tiene que hacer, se evita todo lo posible. Por ello el compilador cogerá nuestras instrucciones y las transformará de una manera transparente a la máquina. También nos dirá si hay algo de la gramática que está mal, ya que si no entiende bien las instrucciones nos dirá que hay un error.

Sé que puede parecer raro que en la lección no se haya visto código, pero creo que es una equivocación empezar la casa por el tejado, por mucha prisa que tengan los alumnos, y que los conceptos deben estar claros para que el alumno entienda lo que está haciendo para poder hacerlo bien.

Un saludo y para cualquier duda, escribid un comentario.

Introducción

Para todo viajero perdido que dé con este lugar y no entienda muy bien por qué o cuál es el propósito de su existencia, sólo indicar que éstas son unas lecciones desde el comienzo sobre programación con una mecánica sencilla: cada entrada es una lección y las dudas se escriben como comentarios de las entradas.

Poco más que añadir, invitado queda cualquier interesado.