{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "3de1e1e0",
   "metadata": {},
   "source": [
    "[← Anterior](01_tipos_datos_y_variables.ipynb) · [Índice](README.md) · [Siguiente →](03_estructuras_de_datos.ipynb)\n",
    "\n",
    "# 02 · Control del flujo\n",
    "\n",
    "**Audiencia.** Personas que ya han visto algo de programación y quieren repasar Python de forma práctica.  \n",
    "**Prerrequisitos.** Tipos básicos, variables, comparaciones y booleanos.  \n",
    "**Duración orientativa.** 60–90 minutos, incluida la práctica.\n",
    "\n",
    "## Objetivos de aprendizaje\n",
    "\n",
    "- Tomar decisiones con `if`, `elif`, `else` y `match`.\n",
    "- Repetir operaciones con `for` y `while`.\n",
    "- Usar `range`, `enumerate`, `zip`, `break` y `continue`.\n",
    "- Elegir una estructura de control clara y evitar bucles infinitos.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "140a024f",
   "metadata": {},
   "source": [
    "## Itinerario\n",
    "\n",
    "1. Condicionales\n",
    "2. Patrones\n",
    "3. Bucles `for`\n",
    "4. Bucles `while`\n",
    "5. Control de iteración\n",
    "6. Práctica\n",
    "\n",
    "> **Cómo trabajar:** ejecuta las celdas en orden, predice el resultado antes de verlo y modifica los ejemplos. Cada bloque termina con un ejercicio, un punto de partida y una solución posible.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "497f953d",
   "metadata": {},
   "source": [
    "## 1. Decisiones con `if`\n",
    "\n",
    "Python ejecuta un bloque según el valor lógico de una condición. Los bloques se delimitan con **indentación**, normalmente cuatro espacios. Solo se ejecuta la primera rama verdadera.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "75da5588",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-02T22:29:43.718827Z",
     "iopub.status.busy": "2026-08-02T22:29:43.718714Z",
     "iopub.status.idle": "2026-08-02T22:29:43.722177Z",
     "shell.execute_reply": "2026-08-02T22:29:43.721868Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "aprobado\n"
     ]
    }
   ],
   "source": [
    "nota = 7.4\n",
    "\n",
    "if nota >= 9:\n",
    "    nivel = \"sobresaliente\"\n",
    "elif nota >= 5:\n",
    "    nivel = \"aprobado\"\n",
    "else:\n",
    "    nivel = \"pendiente\"\n",
    "\n",
    "print(nivel)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "21f81798",
   "metadata": {},
   "source": [
    "Los valores vacíos son falsos, de modo que `if elementos:` suele ser más idiomático que `if len(elementos) > 0:`. Una expresión condicional sirve para decisiones sencillas que producen un valor.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "53eb6309",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-02T22:29:43.723904Z",
     "iopub.status.busy": "2026-08-02T22:29:43.723791Z",
     "iopub.status.idle": "2026-08-02T22:29:43.725979Z",
     "shell.execute_reply": "2026-08-02T22:29:43.725726Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hay trabajo\n",
      "Primera tarea: leer\n"
     ]
    }
   ],
   "source": [
    "tareas = [\"leer\", \"practicar\"]\n",
    "estado = \"hay trabajo\" if tareas else \"lista vacía\"\n",
    "print(estado)\n",
    "\n",
    "if tareas:\n",
    "    print(\"Primera tarea:\", tareas[0])\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "67dac385",
   "metadata": {},
   "source": [
    "## 2. Selección estructural con `match`\n",
    "\n",
    "Desde Python 3.10, `match` compara un valor con patrones. Es útil para comandos o estructuras con formas bien definidas. `_` es el caso por defecto. Para unas pocas comparaciones simples, `if/elif` sigue siendo perfectamente válido.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "4506f226",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-02T22:29:43.727609Z",
     "iopub.status.busy": "2026-08-02T22:29:43.727491Z",
     "iopub.status.idle": "2026-08-02T22:29:43.730112Z",
     "shell.execute_reply": "2026-08-02T22:29:43.729845Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hola, Ana\n",
      "11\n",
      "comando desconocido\n"
     ]
    }
   ],
   "source": [
    "def interpretar_comando(comando):\n",
    "    match comando.split():\n",
    "        case [\"saludar\", nombre]:\n",
    "            return f\"Hola, {nombre}\"\n",
    "        case [\"sumar\", a, b]:\n",
    "            return int(a) + int(b)\n",
    "        case [\"salir\"]:\n",
    "            return \"fin\"\n",
    "        case _:\n",
    "            return \"comando desconocido\"\n",
    "\n",
    "for comando in [\"saludar Ana\", \"sumar 4 7\", \"ayuda\"]:\n",
    "    print(interpretar_comando(comando))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f2f06e61",
   "metadata": {},
   "source": [
    "## 3. Iteración con `for`\n",
    "\n",
    "Un `for` recorre cualquier **iterable**: cadena, rango, lista, diccionario, archivo y muchos otros objetos. No hace falta gestionar manualmente un contador.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "19fd3ea7",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-02T22:29:43.731807Z",
     "iopub.status.busy": "2026-08-02T22:29:43.731679Z",
     "iopub.status.idle": "2026-08-02T22:29:43.733735Z",
     "shell.execute_reply": "2026-08-02T22:29:43.733486Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Tras sumar 1: 1\n",
      "Tras sumar 2: 3\n",
      "Tras sumar 3: 6\n",
      "Tras sumar 4: 10\n",
      "Tras sumar 5: 15\n"
     ]
    }
   ],
   "source": [
    "acumulado = 0\n",
    "for numero in range(1, 6):  # 1, 2, 3, 4, 5\n",
    "    acumulado += numero\n",
    "    print(f\"Tras sumar {numero}: {acumulado}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dabfb2d0",
   "metadata": {},
   "source": [
    "`enumerate` añade un índice y `zip` recorre varios iterables en paralelo. Ambos evitan manejar posiciones a mano.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "1c90e948",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-02T22:29:43.735134Z",
     "iopub.status.busy": "2026-08-02T22:29:43.735029Z",
     "iopub.status.idle": "2026-08-02T22:29:43.737157Z",
     "shell.execute_reply": "2026-08-02T22:29:43.736903Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1. Ada: 95\n",
      "2. Guido: 88\n",
      "3. Grace: 91\n"
     ]
    }
   ],
   "source": [
    "nombres = [\"Ada\", \"Guido\", \"Grace\"]\n",
    "puntuaciones = [95, 88, 91]\n",
    "\n",
    "for posicion, (nombre, puntos) in enumerate(zip(nombres, puntuaciones), start=1):\n",
    "    print(f\"{posicion}. {nombre}: {puntos}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1c6e7261",
   "metadata": {},
   "source": [
    "## 4. Repetición con `while`\n",
    "\n",
    "`while` repite mientras la condición sea verdadera. Es adecuado cuando no se conoce de antemano el número de iteraciones. El bloque debe acercar el estado a la condición de salida.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "db1d7d7a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-02T22:29:43.738576Z",
     "iopub.status.busy": "2026-08-02T22:29:43.738477Z",
     "iopub.status.idle": "2026-08-02T22:29:43.740242Z",
     "shell.execute_reply": "2026-08-02T22:29:43.739983Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Operaciones: 3; saldo restante: 10\n"
     ]
    }
   ],
   "source": [
    "saldo = 100\n",
    "retirada = 30\n",
    "operaciones = 0\n",
    "\n",
    "while saldo >= retirada:\n",
    "    saldo -= retirada\n",
    "    operaciones += 1\n",
    "\n",
    "print(f\"Operaciones: {operaciones}; saldo restante: {saldo}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "10ae95e9",
   "metadata": {},
   "source": [
    "## 5. `break`, `continue` y `else`\n",
    "\n",
    "- `break` abandona el bucle.\n",
    "- `continue` salta a la siguiente iteración.\n",
    "- El `else` de un bucle se ejecuta si termina sin `break`; es útil en búsquedas.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "e8d89d27",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-02T22:29:43.741616Z",
     "iopub.status.busy": "2026-08-02T22:29:43.741521Z",
     "iopub.status.idle": "2026-08-02T22:29:43.743334Z",
     "shell.execute_reply": "2026-08-02T22:29:43.743121Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[6, 16]\n"
     ]
    }
   ],
   "source": [
    "datos = [3, -1, 8, 0, 12, 15]\n",
    "procesados = []\n",
    "\n",
    "for valor in datos:\n",
    "    if valor < 0:\n",
    "        continue\n",
    "    if valor == 0:\n",
    "        break\n",
    "    procesados.append(valor * 2)\n",
    "\n",
    "print(procesados)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "87f1637a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-02T22:29:43.744559Z",
     "iopub.status.busy": "2026-08-02T22:29:43.744486Z",
     "iopub.status.idle": "2026-08-02T22:29:43.746138Z",
     "shell.execute_reply": "2026-08-02T22:29:43.745927Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Encontrado en 3\n"
     ]
    }
   ],
   "source": [
    "numeros = [9, 15, 21, 28, 33]\n",
    "buscado = 28\n",
    "\n",
    "for posicion, numero in enumerate(numeros):\n",
    "    if numero == buscado:\n",
    "        print(\"Encontrado en\", posicion)\n",
    "        break\n",
    "else:\n",
    "    print(\"No encontrado\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c8d2284b",
   "metadata": {},
   "source": [
    "## 6. Bucles anidados y comprensiones\n",
    "\n",
    "Los bucles se pueden anidar, aunque más de dos niveles suelen pedir una función. Para transformaciones simples, una comprensión expresa «produce esto para cada elemento que cumpla la condición».\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "f174f43c",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-02T22:29:43.747529Z",
     "iopub.status.busy": "2026-08-02T22:29:43.747455Z",
     "iopub.status.idle": "2026-08-02T22:29:43.749656Z",
     "shell.execute_reply": "2026-08-02T22:29:43.749476Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)]\n",
      "[0, 4, 16, 36, 64]\n"
     ]
    }
   ],
   "source": [
    "coordenadas = []\n",
    "for fila in range(2):\n",
    "    for columna in range(3):\n",
    "        coordenadas.append((fila, columna))\n",
    "\n",
    "cuadrados_pares = [n ** 2 for n in range(10) if n % 2 == 0]\n",
    "print(coordenadas)\n",
    "print(cuadrados_pares)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3345bf8b",
   "metadata": {},
   "source": [
    "## Errores habituales\n",
    "\n",
    "- Olvidar actualizar la condición de un `while` y crear un bucle infinito.\n",
    "- Modificar una lista mientras se recorre; recorre una copia o construye otra lista.\n",
    "- Añadir demasiadas ramas y niveles de indentación; extrae funciones pequeñas.\n",
    "- Usar `break` sin dejar claro por qué se abandona el bucle.\n",
    "\n",
    "**Extensión opcional:** investiga `itertools` para combinar iteradores de forma eficiente.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8357e054",
   "metadata": {},
   "source": [
    "## Práctica · FizzBuzz configurable\n",
    "\n",
    "Crea `fizzbuzz(limite)` que devuelva una lista desde 1 hasta `limite`:\n",
    "\n",
    "- múltiplos de 3 → `\"Fizz\"`;\n",
    "- múltiplos de 5 → `\"Buzz\"`;\n",
    "- múltiplos de ambos → `\"FizzBuzz\"`;\n",
    "- el resto → el propio número.\n",
    "\n",
    "Valida mentalmente los valores 1, 3, 5 y 15 antes de ejecutar.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "04997bd2",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-02T22:29:43.750900Z",
     "iopub.status.busy": "2026-08-02T22:29:43.750829Z",
     "iopub.status.idle": "2026-08-02T22:29:43.752579Z",
     "shell.execute_reply": "2026-08-02T22:29:43.752366Z"
    },
    "tags": [
     "ejercicio"
    ]
   },
   "outputs": [],
   "source": [
    "def fizzbuzz(limite):\n",
    "    resultado = []\n",
    "    # TODO: recorre el rango y añade el valor apropiado.\n",
    "    return resultado\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "415f5f4a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-08-02T22:29:43.753751Z",
     "iopub.status.busy": "2026-08-02T22:29:43.753681Z",
     "iopub.status.idle": "2026-08-02T22:29:43.756026Z",
     "shell.execute_reply": "2026-08-02T22:29:43.755818Z"
    },
    "tags": [
     "solucion"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 'Fizz', 4, 'Buzz', 'Fizz', 7, 8, 'Fizz', 'Buzz', 11, 'Fizz', 13, 14, 'FizzBuzz', 16]\n"
     ]
    }
   ],
   "source": [
    "def fizzbuzz(limite):\n",
    "    resultado = []\n",
    "    for numero in range(1, limite + 1):\n",
    "        if numero % 15 == 0:\n",
    "            resultado.append(\"FizzBuzz\")\n",
    "        elif numero % 3 == 0:\n",
    "            resultado.append(\"Fizz\")\n",
    "        elif numero % 5 == 0:\n",
    "            resultado.append(\"Buzz\")\n",
    "        else:\n",
    "            resultado.append(numero)\n",
    "    return resultado\n",
    "\n",
    "muestra = fizzbuzz(16)\n",
    "print(muestra)\n",
    "assert muestra[2] == \"Fizz\" and muestra[4] == \"Buzz\" and muestra[14] == \"FizzBuzz\"\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e4d16587",
   "metadata": {},
   "source": [
    "            ## Comprobación rápida\n",
    "\n",
    "            - [ ] Puedo expresar decisiones con ramas mutuamente excluyentes.\n",
    "- [ ] Sé elegir entre `for` y `while`.\n",
    "- [ ] Comprendo el efecto de `break` y `continue`.\n",
    "\n",
    "            [← Anterior](01_tipos_datos_y_variables.ipynb) · [Volver al índice](README.md) · [Siguiente →](03_estructuras_de_datos.ipynb)\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
