summaryrefslogtreecommitdiff
path: root/src/03_estructura.md
diff options
context:
space:
mode:
Diffstat (limited to 'src/03_estructura.md')
-rw-r--r--src/03_estructura.md371
1 files changed, 371 insertions, 0 deletions
diff --git a/src/03_estructura.md b/src/03_estructura.md
new file mode 100644
index 0000000..67fe260
--- /dev/null
+++ b/src/03_estructura.md
@@ -0,0 +1,371 @@
+# Estructura del lenguaje
+
+Aunque ya sabes usar python de forma sencilla, aún no hemos tratado el
+comportamiento del lenguaje y cómo se estructura su sintaxis más allá de varios
+ejemplos sencillos y planos. En este apartado trataremos la estructura y las
+diferentes formas de controlar el flujo del programa.
+
+Como en la mayor parte de lenguajes de programación conocidos, python ejecuta
+las órdenes de arriba a abajo, línea por línea.
+
+Para demostrarlo, prueba a abrir un nuevo fichero, llenarlo con este contenido
+y ejecutarlo (`F5`).
+
+``` python
+print("Esta línea va primero")
+print("Esta línea va segundo")
+print("Esta línea va tercero")
+print("Esta línea va cuarto")
+```
+
+Verás que el resultado del programa es siempre el mismo para todas las veces
+que lo ejecutes y siempre salen los resultados en el mismo orden.
+
+Para poder alterar el orden de los comandos, o elegir en función de una
+condición cuales se ejecutan, python dispone de unas estructuras. Pero, antes
+de contarte cuales son, te adelanto su estructura. Normalmente se declaran en
+una línea terminada en `:` y su cuerpo se sangra hacia dentro. La sangría o
+(indentación si lo calcamos del inglés[^indent]) es lo que define dónde empieza
+o termina un *bloque* en python. Las líneas consecutivas sangradas al mismo
+nivel se consideran el mismo *bloque*.
+
+#### Bloques
+
+Los *bloques* de código son conjuntos de órdenes que pertenecen al mismo
+contexto. Sirven para delimitar zonas del programa, cuerpos de sentencias, etc.
+
+- Puedes comenzar nuevos bloques incrementando el nivel de sangría.
+- Los bloques pueden contener otros bloques.
+- Los bloques terminan cuando el sangrado disminuye.
+
+Es **muy importante** sangrar los bloques correctamente, usando una sangría
+coherente. Puedes usar dos espacios, el tabulador, cuatro espacios o lo que
+desees, pero elijas lo que elijas debe ser coherente en todo el documento.
+IDLE y los editores de código en general puede configurarse para usar una
+anchura de indentación concreta, que se insertará cuando pulses la tecla
+tabulador. El estándar de python es cuatro espacios.
+
+[^indent]: <https://en.wikipedia.org/wiki/Indentation_(typesetting)>
+
+## Sintaxis general
+
+La sintaxis de python es sencilla en su concepción pero ha ido complicándose a
+medida que el lenguaje ha ido creciendo. En este apartado únicamente se
+mencionan los puntos más comunes de la sintaxis, dejando para futuros apartados
+los detalles específicos de conceptos que aún no se han explicado.
+
+### Comentarios
+
+Los comentarios son *fundamentales* en el código fuente. El intérprete los
+ignora pero son primordiales para explicar detalles de nuestro código a otros
+programadores o a nosotros mismos en el futuro. Comentar bien el código fuente
+es un arte en sí mismo.
+
+Los comentarios en python se introducen con el símbolo `#`. Desde su aparición
+hasta el final de la línea se considera un comentario y python lo descarta.
+Pueden iniciarse a mitad de línea o en el inicio, tal y como se muestra a
+continuación:
+
+``` python
+# En este ejemplo se muestran comentarios, como este mismo
+print("Hola") # Esto es otro comentario
+```
+
+### Control de flujo
+
+Como ya se ha adelantado, es posible cambiar el orden de ejecución del programa
+en función de unas normas o evitar que el programa ejecute ciertas sentencias.
+A esto se le conoce como *control de flujo*.
+
+#### Condicionales
+
+Las condicionales son herramientas de *control de flujo* que permiten separar
+la ejecución en diferentes ramas en función de unas condiciones. En python sólo
+existe el condicional `if` (si, en castellano), aunque existen otras
+estructuras para conseguir el mismo efecto, no las trataremos aún.
+
+Ésta es la sintaxis del `if`:
+
+``` python
+if condición:
+ # Este bloque se ejecuta si la condición se cumple
+elif condiciónN:
+ # Este bloque (opcional) se ejecuta si las condiciones previas no se
+ # cumplen y la condiciónN sí se cumple
+else:
+ # Este bloque (opcional) se ejecuta si todas las condiciones previas no
+ # se cumplen
+```
+
+Tal y como se muestra en el ejemplo, los bloques `elif` y el bloque `else` son
+opcionales. Es posible, y muy común además, hacer un `if` únicamente con el
+apartado inicial.
+
+Si te preguntas qué condiciones debes usar, es tan simple como usar sentencias
+de python cuyo resultado sea `True` o `False`. Cuando la sentencia resulte en
+un `True` la condición se cumplirá y el bloque interior se ejecutará.
+
+En resumen, el *if* ejecuta el bloque *si* la condición se cumple.
+
+#### Bucles
+
+Existen dos tipos de sentencia para hacer repeticiones en python, ambas son
+similares al `if`, pero en lugar de elegir si una pieza de código se ejecuta o
+no, lo que deciden es si es necesario repetirla en función de una condición.
+
+##### While
+
+El `while` (mientras que) es la más sencilla de estas estructuras, y la menos
+usada.
+
+``` python
+while condición:
+ # Este bloque se ejecutará siempre que la condición se considere verdadera
+```
+
+El `while` comprueba la condición en primer lugar, si resulta en `True` ejecuta
+el bloque interno y vuelve a comprobar la condición. Si es `True`, ejecuta el
+bloque de nuevo, y así sucesivamente.
+
+Es decir, ejecuta el bloque *mientras que* la condición se cumple.
+
+##### For
+
+Los bucles *for* (para) son los más complejos y más usados,
+
+```
+for preparación:
+ # Bloque a repetir si la preparación funciona con el contexto creado por la
+ # preparación
+else:
+ # Bloque (opcional) a ejecutar si el bloque cuerpo no termina de forma
+ # abrupta con un `break`
+```
+
+No te preocupes ahora mismo por el `else`, ya que se suele considerar python
+avanzado y no suele usarse. Más adelante en este capítulo analizaremos un
+ejemplo.
+
+En lo que a la preparación se refiere, el `for` es relativamente peculiar,
+sirve para ejecutar el primer bloque para un contexto concreto, el creado por
+una sentencia de preparación. Si la preparación falla el bucle se rompe.
+
+El uso más común del `for` es el de iterar en secuencias gracias al operador
+`in` que mencionamos anteriormente pero que en este caso toma un uso distinto:
+
+```
+>>> for i in [0,1,2,3]:
+... i+2
+...
+2
+3
+4
+5
+```
+
+Como puedes ver, el bloque interno `i+2` se ejecuta en cuatro ocasiones,
+cambiando el valor de `i` a los valores internos de la lista `[0,1,2,3]`.
+Cuando la lista termina, la preparación falla porque no quedan elementos y el
+bucle se rompe.
+
+Este último ejemplo es una receta de amplio uso que te aconsejo memorizar.
+
+
+#### NOTA: truthy and falsey
+
+En este tipo de sentencias donde se comprueba si una condición es verdadera o
+falsa, python automáticamente trata de convertir el resultado a Boolean usando
+la función `bool` que ya conoces del apartado sobre tipos. No es necesario que
+conviertas a Boolean manualmente ya que estas sentencias disponen de una
+conversión implícita a Boolean[^truthy].
+
+Por tanto, cualquier resultado que tras pasar por la función `bool` dé como
+resultado `True` será considerado verdadero y viceversa. A estos valores se les
+conoce habitualmente como *truthy* y *falsey* porque no son `True` o `False`
+pero se comportan como si lo fueran. Algunos ejemplos curiosos:
+
+```
+>>> bool([])
+False
+>>> bool(None)
+False
+>>> bool("")
+False
+>>> bool(" ")
+True
+>>> bool([None])
+True
+```
+
+Es relativamente sencillo prever qué valores son *truthy* o *falsey*,
+normalmente los valores que representan un vacío se consideran `False`.
+
+[^truthy]:
+ <https://docs.python.org/3/library/stdtypes.html#truth-value-testing>
+
+
+### List comprehensions
+
+Una de las excepciones sintácticas que sí que podemos explicar en este momento,
+en el que ya sabes hacer bucles, son las *list comprehensions*. Python dispone
+de un sistema para crear secuencias y transformarlas muy similar a la notación
+de construcción de sets de las matemáticas[^set-notation].
+
+Como mejor se entiende es con unos ejemplos, en este caso vamos usar la función
+`range` para crear una lista de números del `0` (inclusive) al `10` (no
+inclusive). Usando la ayuda puedes saber más sobre la función `range`.
+
+```
+>>> [i**2 for i in range(0, 10)]
+[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
+>>> tuple(i**2 for i in range(0, 10))
+(0, 1, 4, 9, 16, 25, 36, 49, 64, 81)
+>>> { str(i): i**2 for i in range(0, 10)}
+{'0': 0, '1': 1, '2': 4, '3': 9, '4': 16, '5': 25, '6': 36, '7': 49, '8': 64,
+'9': 81}
+
+>>> [i**2 for i in range(0, 10) if i > 5 ]
+[36, 49, 64, 81]
+```
+
+Como ves, en el caso de los diccionarios es necesario crear las claves también.
+En este caso las creamos convirtiendo el propio número a string con la función
+`str`.
+
+Como ves en los primeros ejemplos, de una secuencia de números hemos creado una
+secuencia de números al cuadrado. Pero las *list comprehensions* son más
+poderosas que eso, pudiendo a llegar a complicarse sobremanera añadiendo
+condiciones, como en el último de los ejemplos, para filtrar algunos casos.
+
+Te habrá mosqueado el uso de `tuple` para crear la tupla. Todo tiene una razón,
+la tupla, al estar formada por paréntesis, python no tiene claro si son
+paréntesis de precedencia o de creación de tupla, y considera que son los
+primeros, dando como resultado un generador, un paso intermedio de nuestro
+proceso que dejamos para el futuro.
+
+```
+>>> (i**2 for i in range(0, 10))
+<generator object <genexpr> at 0x7f779d9b2d58>
+```
+
+[^set-notation]: <https://en.wikipedia.org/wiki/Set-builder_notation>
+
+### Excepciones
+
+TODO
+
+### Funciones
+
+TODO
+
+
+### Sentencias útiles
+
+Python dispone de un conjunto de sentencias que pueden facilitar y flexibilizar
+mucho el uso de las estructuras que acabamos de visitar.
+
+#### Pass
+
+`pass` es una sentencia vacía, que no ejecuta nada. Es necesaria debido a las
+normas de sangría de python. Si construyes un bloque y no quieres rellenarlo
+por la razón que sea, debes usar `pass` en su interior porque, si no lo haces y
+simplemente lo dejas vacío, la sintaxis será incorrecta y python lanzará una
+excepción grave diciéndote que esperaba un bloque con sangría y no se lo diste.
+
+```
+>>> if True:
+...
+ File "<stdin>", line 2
+
+ ^
+IndentationError: expected an indented block
+>>> if True:
+... pass
+...
+```
+
+Suele utilizarse cuando no quiere tratarse una excepción o cuando se ha hecho
+un boceto de una función que aún no quiere desarrollarse, para que el
+intérprete no falle de forma inevitable.
+
+> NOTA: Las excepciones de sintaxis son las más graves, implican que el
+> intérprete no es capaz de entender lo que le pedimos así que la ejecución del
+> programa no llega a realizarse. La sintaxis se comprueba en una etapa previa
+> a la ejecución.
+
+#### Continue
+
+`continue` sirve para terminar el bucle actual y volver a comprobar la
+condición para decidir si volver a ejecutarlo.
+
+En el siguiente ejemplo sirve para saltar la ejecución para el caso en el que
+`i` es `2`.
+
+```
+>>> for i in [0, 1, 2, 3]:
+... if i == 2:
+... continue
+... i
+...
+0
+1
+3
+```
+
+#### Break
+
+`break` rompe el bucle actual. A diferencia del `continue`, no se intenta
+ejecutar la siguiente sentencia, simplemente se rompe el bucle completo. En el
+siguiente ejemplo se rompe el bucle cuando `i` es `2` y no se recupera.
+
+```
+>>> for i in [0, 1, 2, 3]:
+... if i == 2:
+... break
+... i
+...
+0
+1
+```
+
+El `break` se usa mucho para romper bucles que son infinitos por definición. En
+lugar de añadir una condición compleja a un bucle `while`, por ejemplo, puedes
+usar un `True` en su condición e introducir un `if` más simple dentro de éste
+con un `break` que termine el bucle en los casos que te interesen. O puedes
+capturar una excepción y romper el bucle si ocurre.
+
+Además, es muy usado en búsquedas y habilita el uso del `else` en las
+sentencias `for`. Te propongo como ejercicio que trates de ejecutar y
+comprender el funcionamiento de esta pieza de código de python avanzado antes
+de seguir leyendo:
+
+``` python
+text = "texto de prueba"
+pos = 0
+
+for i in text:
+ if i == "b":
+ print("Found b in position: " + str(pos))
+ break
+ pos = pos + 1
+else:
+ print("b not found :( ")
+```
+
+Si cambias el texto de la variable `text` por uno que no tenga letra `b` verás
+que el bloque `else` se ejecuta. Esto se debe a que el `for` no termina de
+forma abrupta, sino que itera por el string completo.
+
+Si quieres, puedes memorizar esta estructura para cuando quieras hacer
+búsquedas. Es elegante y te será útil.
+
+
+#### Return
+
+La sentencia `return` sólo tiene sentido dentro de las funciones. Sirve para
+finalizar la ejecución de una función sustituyendo su llamada por el resultado
+indicado en el `return`. Esta operación rompe todos los bucles por completo.
+
+En el apartado de las funciones profundizaremos en el uso del `return` pero es
+importante mencionarlo aquí porque su funcionalidad puede sustituir al `break`
+en muchas ocasiones.