Cerrábamos la lección anterior con tareafacil.py funcionando y una limitación evidente: todo lo que hace es imprimir texto fijo, y cambiar un dato obliga a editar el código a mano. La tentación ahora es lanzarse a aprender sintaxis para arreglarlo. Vamos a resistirla una lección más, porque falta la habilidad que de verdad separa a quien programa de quien copia código: pensar la solución antes de escribirla.
Un programador con prisa abre el editor y empieza a teclear. A las dos horas tiene código que casi funciona, no sabe por qué falla y no puede explicarle a nadie qué hace. Un programador con oficio dedica los primeros veinte minutos a papel: entiende el problema, lo descompone, escribe los pasos y los comprueba a mano. Después teclea, y teclea poco, porque ya sabe qué va a escribir.
En esta lección aprenderás qué es exactamente un algoritmo, cómo descomponer un problema, cómo expresar una solución en pseudocódigo y en diagrama de flujo, y cómo verificarla sin ordenador mediante una prueba de escritorio. Todo aplicado a dos operaciones reales de TareaFácil.
Aparecerán construcciones como «repetir» o «si...entonces»: aquí las usaremos solo como forma de pensar. Escribirlas en Python es asunto de los módulos 2 y 3.
Contenido
- Qué es un algoritmo
- Las cinco propiedades de un algoritmo
- Descomponer un problema en pasos y subproblemas
- Pseudocódigo: convenciones y ejemplo
- Diagramas de flujo
- Prueba de escritorio: verificar sin ordenador
- Del pseudocódigo al código Python
- TareaFácil: registrar una tarea nueva
- TareaFácil: qué tarea atiende Marta primero
- Errores comunes y consejos
- Ejercicios
- Conclusión
- Qué es un algoritmo
Un algoritmo es una secuencia finita de pasos precisos que, partiendo de unos datos de entrada, produce un resultado en un tiempo finito.
La palabra suena técnica, pero el concepto es cotidiano: una receta de cocina, las instrucciones de montaje de una estantería y el procedimiento de la multiplicación que aprendiste en el colegio son algoritmos. Comparten lo esencial: pasos ordenados, sin ambigüedad, que terminan y producen algo.
Lo importante es esto: un algoritmo es independiente del lenguaje de programación. El mismo algoritmo para ordenar una lista se puede escribir en Python, en Java o en papel; lo que cambia es la notación, no la idea. Por eso lo que aprendas aquí te servirá aunque cambies de lenguaje diez veces.
| Concepto | Qué es | Analogía |
|---|---|---|
| Problema | Lo que hay que resolver | «Quiero un pastel» |
| Algoritmo | El método para resolverlo | La receta |
| Programa | El algoritmo escrito en un lenguaje ejecutable | La receta traducida a órdenes para un robot de cocina |
| Ejecución | El programa funcionando con datos concretos | Hacer el pastel el sábado |
Confundir estas cuatro cosas es el origen de mucha frustración. «No me sale el programa» suele significar en realidad «no tengo claro el algoritmo», y eso no se arregla tocando código.
- Las cinco propiedades de un algoritmo
No toda lista de pasos es un algoritmo. Debe cumplir cinco propiedades.
1. Preciso. Cada paso indica exactamente qué hacer, sin margen de interpretación.
- Mal: «revisa las tareas importantes».
- Bien: «para cada tarea, si su prioridad es alta y su estado es pendiente, muéstrala por pantalla».
2. Definido (determinista). Con las mismas entradas produce siempre las mismas salidas. Si ejecutas el algoritmo dos veces con los mismos datos, el resultado es idéntico.
3. Finito. Termina después de un número finito de pasos. Un algoritmo que no termina no es un algoritmo, es un bucle infinito, y es uno de los errores más habituales al empezar.
- Mal: «repite: cuenta una tarea más» (nunca acaba).
- Bien: «repite mientras queden tareas por revisar» (acaba, porque las tareas son finitas y en cada vuelta queda una menos).
4. Con entrada definida. Cero o más datos de partida, especificados con claridad. En «registrar una tarea», la entrada son el título, la descripción, el responsable y la prioridad.
5. Con salida definida. Al menos un resultado observable: un valor, un mensaje en pantalla, un fichero modificado. Un algoritmo que no produce nada observable no sirve para nada.
| Propiedad | Pregunta de control | Si falla... |
|---|---|---|
| Preciso | ¿Puede alguien interpretar este paso de dos formas? | El programa hará algo inesperado |
| Definido | ¿Dará siempre el mismo resultado con los mismos datos? | Errores imposibles de reproducir |
| Finito | ¿Está garantizado que termina? | El programa se cuelga |
| Entrada definida | ¿Qué datos necesito y de dónde salen? | Faltarán datos a mitad de ejecución |
| Salida definida | ¿Qué produce y cómo lo veo? | Nadie sabrá si funcionó |
Aplica estas cinco preguntas a cualquier algoritmo que escribas. Detectan la mayoría de los fallos antes de que existan.
- Descomponer un problema en pasos y subproblemas
La técnica central para atacar cualquier problema se llama descomposición: partir un problema grande en problemas más pequeños hasta que cada pieza sea evidente. Se resuelve cada pieza por separado y luego se combinan.
Aplicado a TareaFácil, la petición inicial de Marta era: «quiero un programa para gestionar las tareas del estudio». Así, en bloque, es inabordable. Descompuesta:
flowchart TD
A["Gestionar las tareas de Estudio Alba"] --> B["Registrar una tarea"]
A --> C["Consultar tareas"]
A --> D["Actualizar tareas"]
A --> E["Guardar la informacion"]
B --> B1["Pedir los datos"]
B --> B2["Validar prioridad y responsable"]
B --> B3["Anadir a la lista"]
C --> C1["Listar todas"]
C --> C2["Filtrar por responsable"]
C --> C3["Filtrar por estado"]
D --> D1["Marcar como completada"]
D --> D2["Cambiar responsable"]
Cada hoja del árbol es una tarea pequeña que sí sabemos abordar. «Marcar como completada» es un problema manejable; «gestionar las tareas del estudio» no lo era.
La descomposición aporta tres ventajas concretas:
- Progreso visible. Resolver una hoja del árbol lleva minutos, y ves avance. Enfrentarte al problema entero produce parálisis.
- Errores localizados. Si falla el filtrado por responsable, sabes exactamente qué pieza mirar.
- Reutilización. «Validar prioridad» sirve tanto al registrar una tarea como al modificarla. Escribirlo una vez y usarlo dos veces es la base del módulo 4.
Un criterio práctico para saber cuándo parar de descomponer: cuando puedas explicar la pieza en una frase y sepas cómo hacerla a mano. Si aún dudas, sigue partiendo.
- Pseudocódigo: convenciones y ejemplo
El pseudocódigo es una forma de escribir algoritmos a medio camino entre el español y el código: lo bastante estructurado para no ser ambiguo, lo bastante libre para no pelearse con la sintaxis.
No existe un pseudocódigo oficial. Estas son las convenciones que usaremos en el curso:
| Elemento | Notación | Ejemplo |
|---|---|---|
| Inicio y fin | INICIO / FIN |
|
| Entrada de datos | LEER <dato> |
LEER titulo |
| Salida de datos | MOSTRAR <mensaje> |
MOSTRAR "Tarea guardada" |
| Asignar un valor | <nombre> ← <valor> |
contador ← 0 |
| Condición | SI <cond> ENTONCES ... SINO ... FIN SI |
|
| Repetición condicional | MIENTRAS <cond> HACER ... FIN MIENTRAS |
|
| Repetición sobre elementos | PARA CADA <x> EN <lista> HACER ... FIN PARA |
|
| Comentario | // texto |
// se ignora al ejecutar |
Dos reglas de estilo importantes: indenta el contenido de cada bloque (facilita ver dónde empieza y acaba) y cierra siempre los bloques (FIN SI, FIN MIENTRAS).
Ejemplo completo. Algoritmo que calcula cuántas tareas pendientes tiene el estudio:
INICIO
// Entrada: una lista de tareas, cada una con su estado
// Salida: el numero de tareas pendientes
pendientes ← 0
PARA CADA tarea EN lista_de_tareas HACER
SI estado de tarea = "pendiente" ENTONCES
pendientes ← pendientes + 1
FIN SI
FIN PARA
MOSTRAR "Tareas pendientes: " + pendientes
FINRepasemos las cinco propiedades sobre este algoritmo:
- Preciso: cada paso es inequívoco.
- Definido: la misma lista produce siempre el mismo número.
- Finito: la lista tiene un número finito de elementos y cada vuelta consume uno.
- Entrada definida: la lista de tareas con sus estados.
- Salida definida: un mensaje con el número de pendientes.
Cumple las cinco. Es un algoritmo correcto, y lo hemos verificado sin encender el ordenador.
- Diagramas de flujo
Un diagrama de flujo representa el algoritmo gráficamente. Es especialmente útil cuando hay decisiones y caminos alternativos, porque se ven de un vistazo.
Los símbolos básicos:
| Símbolo | Forma | Significado |
|---|---|---|
| Inicio / Fin | Óvalo | Dónde empieza y acaba |
| Proceso | Rectángulo | Una acción o cálculo |
| Decisión | Rombo | Una pregunta con salidas Sí/No |
| Entrada / Salida | Romboide | Leer datos o mostrar resultados |
| Flecha | — | Orden en que se avanza |
Ejemplo 1: ¿la tarea es urgente? Un algoritmo con una sola decisión.
flowchart TD
A(["INICIO"]) --> B[/"LEER prioridad de la tarea"/]
B --> C{"prioridad = alta?"}
C -->|Si| D[/"MOSTRAR: Atender hoy"/]
C -->|No| E[/"MOSTRAR: Puede esperar"/]
D --> F(["FIN"])
E --> F
Fíjate en dos cosas: del rombo salen exactamente dos flechas etiquetadas, y ambos caminos vuelven a unirse antes del fin. Un diagrama con caminos que no acaban en FIN está incompleto.
Ejemplo 2: contar las tareas pendientes. El mismo algoritmo del apartado anterior, ahora con repetición.
flowchart TD
A(["INICIO"]) --> B["pendientes ← 0"]
B --> C["Situarse en la primera tarea"]
C --> D{"Quedan tareas<br/>por revisar?"}
D -->|No| H[/"MOSTRAR pendientes"/]
D -->|Si| E{"Su estado es<br/>pendiente?"}
E -->|Si| F["pendientes ← pendientes + 1"]
E -->|No| G["Pasar a la siguiente tarea"]
F --> G
G --> D
H --> I(["FIN"])
El bucle se ve como lo que es: una flecha que vuelve hacia atrás, de G a D. Y ahí está la clave de la finitud: G avanza a la siguiente tarea en cada vuelta, así que en algún momento la respuesta a D será «No». Si G no existiera, el diagrama giraría para siempre. Esa flecha de retorno es el bucle infinito hecho visible, y es un buen motivo para dibujar antes de programar.
| Herramienta | Cuándo conviene |
|---|---|
| Pseudocódigo | Algoritmos largos, muchos pasos secuenciales, cerca ya del código |
| Diagrama de flujo | Pocos pasos pero muchas decisiones; explicar la lógica a otra persona |
En la práctica se usan las dos: el diagrama para entender la forma general, el pseudocódigo para el detalle.
- Prueba de escritorio: verificar sin ordenador
La prueba de escritorio (o trazado) consiste en ejecutar el algoritmo a mano, con datos concretos, anotando en una tabla cómo cambia cada valor paso a paso. Es la técnica más infravalorada y más rentable de todo el oficio: detecta errores de lógica en cinco minutos que en el ordenador costarían una hora.
Tracemos el algoritmo de contar pendientes con estos datos de Estudio Alba:
| Nº | Tarea | Responsable | Estado |
|---|---|---|---|
| 1 | Logotipo Panadería Solé | Luis | pendiente |
| 2 | Cartel feria del libro | Nuria | completada |
| 3 | Rediseño web cliente Vidal | Luis | pendiente |
| 4 | Presupuesto marzo | Marta | pendiente |
Y ahora la tabla de trazado. Una fila por vuelta del bucle, una columna por valor que nos interesa:
| Vuelta | Tarea examinada | Estado | ¿Es pendiente? | pendientes después |
|---|---|---|---|---|
| — | (inicio) | — | — | 0 |
| 1 | Logotipo Panadería Solé | pendiente | Sí | 1 |
| 2 | Cartel feria del libro | completada | No | 1 |
| 3 | Rediseño web cliente Vidal | pendiente | Sí | 2 |
| 4 | Presupuesto marzo | pendiente | Sí | 3 |
| — | (fin: no quedan tareas) | — | — | 3 |
Salida: Tareas pendientes: 3. Contando a ojo en la tabla de datos: la 1, la 3 y la 4. Correcto.
Cómo hacer una prueba de escritorio bien:
- Elige datos pequeños pero representativos. Cuatro elementos bastan; con cuarenta te cansarás y te saltarás pasos.
- Una columna por cada valor que cambie. Si hay tres variables, tres columnas.
- Una fila por paso o vuelta. Sin agrupar ni «ir más rápido»: el error suele estar precisamente donde uno da algo por supuesto.
- Sé la máquina, no el autor. No hagas lo que querías escribir, haz lo que está escrito. Aquí es donde aparecen los errores.
- Prueba también los casos límite. ¿Y si la lista está vacía? Con nuestro algoritmo, el bucle no se ejecuta ninguna vez y la salida es
0. Correcto. ¿Y si todas están completadas? También0. Correcto.
Ese último punto merece énfasis: los casos límite son donde vive la mayoría de los errores reales. Lista vacía, un solo elemento, todos iguales, valores repetidos. Compruébalos siempre.
- Del pseudocódigo al código Python
Cuando el algoritmo está claro y trazado, traducirlo a Python es casi mecánico. Veámoslo con un ejemplo que ya podemos escribir entero: mostrar la ficha de una tarea.
Pseudocódigo:
INICIO
MOSTRAR "--- FICHA DE TAREA ---"
MOSTRAR "Titulo: Logotipo Panaderia Sole"
MOSTRAR "Responsable: Luis"
MOSTRAR "Prioridad: alta"
MOSTRAR "Estado: pendiente"
FINPython:
print("--- FICHA DE TAREA ---")
print("Titulo: Logotipo Panaderia Sole")
print("Responsable: Luis")
print("Prioridad: alta")
print("Estado: pendiente")La correspondencia es directa: MOSTRAR se convierte en print, y el texto va entre comillas dentro de los paréntesis. INICIO y FIN no se traducen: en Python el programa empieza en la primera línea y acaba en la última.
Esta es la tabla de traducción completa que iremos completando a lo largo del curso:
| Pseudocódigo | Python | Dónde se estudia |
|---|---|---|
MOSTRAR x |
print(x) |
Ya lo sabes |
LEER x |
x = input() |
Entrada y salida |
x ← 5 |
x = 5 |
Variables |
SI ... ENTONCES |
if ...: |
Condicionales |
SINO |
else: |
Condicionales |
MIENTRAS ... HACER |
while ...: |
Bucles |
PARA CADA x EN lista |
for x in lista: |
Bucles |
De momento solo tienes la primera fila. Al terminar el módulo 3 tendrás todas, y podrás traducir a Python cualquier algoritmo que escribas en pseudocódigo. Mientras tanto, escribe pseudocódigo sin miedo: el algoritmo es lo valioso, y traducirlo será lo fácil.
- TareaFácil: registrar una tarea nueva
Vamos a aplicar todo lo anterior al primer requisito de TareaFácil (R1 y R2 de la lista de la primera lección): registrar una tarea con sus cinco datos, validando la prioridad.
El problema. Marta quiere apuntar un encargo nuevo. Hay que pedirle título, descripción, responsable y prioridad; el estado siempre empieza como «pendiente». La prioridad solo puede ser alta, media o baja: si escribe otra cosa, hay que volver a preguntar.
Pseudocódigo:
INICIO
// Entrada: datos tecleados por el usuario
// Salida: la tarea registrada y un mensaje de confirmacion
MOSTRAR "--- NUEVA TAREA ---"
LEER titulo
LEER descripcion
LEER responsable
// Validacion: repetir hasta que la prioridad sea valida
prioridad_valida ← FALSO
MIENTRAS prioridad_valida = FALSO HACER
MOSTRAR "Prioridad (alta / media / baja):"
LEER prioridad
SI prioridad = "alta" O prioridad = "media" O prioridad = "baja" ENTONCES
prioridad_valida ← VERDADERO
SINO
MOSTRAR "Prioridad no valida. Intentalo de nuevo."
FIN SI
FIN MIENTRAS
estado ← "pendiente"
GUARDAR la tarea (titulo, descripcion, responsable, prioridad, estado)
MOSTRAR "Tarea registrada correctamente."
FINDiagrama de flujo:
flowchart TD
A(["INICIO"]) --> B[/"LEER titulo, descripcion,<br/>responsable"/]
B --> C[/"LEER prioridad"/]
C --> D{"prioridad es<br/>alta, media o baja?"}
D -->|No| E[/"MOSTRAR: prioridad no valida"/]
E --> C
D -->|Si| F["estado ← pendiente"]
F --> G["GUARDAR la tarea"]
G --> H[/"MOSTRAR: tarea registrada"/]
H --> I(["FIN"])
Fíjate en la flecha que vuelve de E a C: es el bucle de validación. Mientras la prioridad sea incorrecta, se vuelve a preguntar. Y es finito siempre que el usuario acabe escribiendo algo válido; si teclea disparates indefinidamente, el programa preguntará indefinidamente. Eso no es un error del algoritmo: es una decisión de diseño que conviene tomar conscientemente. Una alternativa sería permitir tres intentos y cancelar.
Prueba de escritorio. Marta registra un encargo y se equivoca al escribir la prioridad:
| Paso | Acción | Valor introducido | Estado del algoritmo |
|---|---|---|---|
| 1 | LEER titulo | «Tarjetas de visita Vidal» | titulo asignado |
| 2 | LEER descripcion | «300 uds, doble cara» | descripcion asignada |
| 3 | LEER responsable | «Nuria» | responsable asignado |
| 4 | LEER prioridad | «urgente» | no es válida → mensaje de error |
| 5 | LEER prioridad | «ALTA» | ¿es válida? |
| 6 | LEER prioridad | «alta» | válida → sale del bucle |
| 7 | estado ← pendiente | — | estado = «pendiente» |
| 8 | GUARDAR y MOSTRAR | — | «Tarea registrada correctamente.» |
El paso 5 revela un problema que la prueba de escritorio ha hecho aflorar: «ALTA» en mayúsculas no coincide con «alta», porque son textos distintos. El algoritmo, tal y como está escrito, la rechazaría. ¿Es lo que queremos? Casi seguro que no: Marta escribirá en mayúsculas la mitad de las veces.
La corrección es sencilla —convertir lo introducido a minúsculas antes de comparar— y la aplicaremos en Conversión de tipos y validación. Lo relevante aquí es cómo hemos encontrado el fallo: con una tabla y un lápiz, antes de escribir una sola línea de Python. Encontrarlo más tarde, con el programa ya escrito, habría costado mucho más.
- TareaFácil: qué tarea atiende Marta primero
Segundo algoritmo, esta vez de decisión. Marta llega el lunes, tiene varias tareas asignadas y quiere saber por cuál empezar.
La regla de negocio, acordada con el equipo:
- Solo cuentan las tareas pendientes: las completadas se ignoran.
- Entre las pendientes, gana la de prioridad más alta (
alta>media>baja). - Si hay empate de prioridad, gana la que se registró antes.
Fíjate en que estas tres reglas no las inventa el programador: se acuerdan con quien tiene el problema. Escribirlas explícitamente es parte del trabajo, y muchas veces la primera vez que el equipo se pone de acuerdo en algo que creía obvio.
Pseudocódigo:
INICIO
// Entrada: lista de tareas de Marta, en orden de registro
// Salida: la tarea que debe atender primero
elegida ← NINGUNA
PARA CADA tarea EN tareas_de_marta HACER
SI estado de tarea = "completada" ENTONCES
// no nos interesa, pasamos a la siguiente
SINO
SI elegida = NINGUNA ENTONCES
elegida ← tarea
SINO
SI prioridad de tarea es mayor que prioridad de elegida ENTONCES
elegida ← tarea
FIN SI
// si es igual o menor, se mantiene la anterior:
// asi gana la registrada antes en caso de empate
FIN SI
FIN SI
FIN PARA
SI elegida = NINGUNA ENTONCES
MOSTRAR "No tienes tareas pendientes."
SINO
MOSTRAR "Empieza por: " + titulo de elegida
FIN SI
FINDiagrama de flujo:
flowchart TD
A(["INICIO"]) --> B["elegida ← NINGUNA"]
B --> C{"Quedan tareas<br/>por revisar?"}
C -->|No| J{"elegida = NINGUNA?"}
C -->|Si| D{"Esta completada?"}
D -->|Si| I["Pasar a la siguiente"]
D -->|No| E{"elegida = NINGUNA?"}
E -->|Si| F["elegida ← tarea actual"]
E -->|No| G{"Su prioridad es mayor<br/>que la de elegida?"}
G -->|Si| F
G -->|No| I
F --> I
I --> C
J -->|Si| K[/"MOSTRAR: no hay pendientes"/]
J -->|No| L[/"MOSTRAR: empieza por elegida"/]
K --> M(["FIN"])
L --> M
Prueba de escritorio. Las tareas de Marta, en orden de registro:
| Nº | Título | Prioridad | Estado |
|---|---|---|---|
| 1 | Presupuesto cliente Vidal | media | pendiente |
| 2 | Revisar facturas febrero | baja | completada |
| 3 | Llamar a Panadería Solé | alta | pendiente |
| 4 | Preparar reunión equipo | alta | pendiente |
Trazado vuelta a vuelta:
| Vuelta | Tarea | ¿Completada? | elegida antes |
¿Sustituye? | elegida después |
|---|---|---|---|---|---|
| 1 | Presupuesto Vidal (media) | No | NINGUNA | Sí, no había ninguna | Presupuesto Vidal |
| 2 | Revisar facturas (baja) | Sí | Presupuesto Vidal | No, se ignora | Presupuesto Vidal |
| 3 | Llamar a Solé (alta) | No | Presupuesto Vidal | Sí: alta > media | Llamar a Solé |
| 4 | Preparar reunión (alta) | No | Llamar a Solé | No: alta no es mayor que alta | Llamar a Solé |
Salida: Empieza por: Llamar a Panadería Solé.
Verifiquemos que es correcto según las reglas acordadas. Pendientes hay tres: Presupuesto (media), Llamar a Solé (alta) y Preparar reunión (alta). De prioridad alta hay dos, y entre ellas gana la registrada antes, que es «Llamar a Solé» (número 3 frente a número 4). Coincide con lo que produce el algoritmo.
Y los casos límite:
- Marta no tiene ninguna tarea. El bucle no se ejecuta,
elegidasigue siendo NINGUNA y se muestra «No tienes tareas pendientes». Correcto. - Todas están completadas. Todas caen por la rama que las ignora,
elegidasigue NINGUNA y sale el mismo mensaje. Correcto. - Solo una pendiente. Se asigna en su vuelta y ninguna la sustituye. Correcto.
El algoritmo aguanta los cuatro escenarios. Está listo para implementarse, y lo haremos cuando tengamos condicionales y bucles en Python.
Errores Comunes y Consejos
Empezar a teclear sin haber pensado el algoritmo. Es el error más caro. Si no sabes explicar en voz alta qué pasos va a dar tu programa, no estás listo para escribirlo. Papel primero.
Pasos ambiguos. «Comprobar si la tarea es importante» no es un paso: no dice qué significa importante. «Comprobar si su prioridad es alta» sí lo es. Toda ambigüedad que dejes en el algoritmo reaparecerá como error en el programa.
Olvidar el avance dentro de un bucle. Si en cada vuelta no cambia nada que acerque al final, el bucle es infinito. En el diagrama se ve enseguida: busca qué caja hace progresar la condición del rombo. Si no la encuentras, tienes un problema.
Saltarse los casos límite. Lista vacía, un solo elemento, todos con el mismo valor, todos completados. Un algoritmo que solo funciona con el caso bonito no funciona.
Hacer la prueba de escritorio «como autor» y no «como máquina». Al trazar tiendes a ejecutar lo que querías escribir, no lo que escribiste. Es la trampa más sutil. Truco: traza un algoritmo tuyo de hace una semana, o intercámbialo con otra persona.
Dar por supuestas reglas de negocio. «Gana la de prioridad más alta» parece completo hasta que dos tareas empatan. Escribe siempre qué pasa en los empates, en los vacíos y en los datos incorrectos. Preguntarlo antes es barato; descubrirlo en producción, no.
Creer que el pseudocódigo debe ser perfecto. No es un entregable, es una herramienta de pensamiento. Táchalo, reescríbelo, usa flechas al margen. Si te ayuda a pensar, está bien escrito.
Consejo final: cuando un programa te salga mal, no empieces cambiando código a ver si acierta. Vuelve al algoritmo y hazle una prueba de escritorio. En la mayoría de los casos el error no está en la sintaxis, está en el razonamiento.
Ejercicios
Ejercicio 1: Detectar propiedades incumplidas
Para cada algoritmo, indica qué propiedad o propiedades incumple y corrígelo:
a)
b)
c)
Ejercicio 2: Algoritmo y diagrama para contar las tareas de una persona
Escribe el pseudocódigo y el diagrama de flujo de un algoritmo que, dada la lista de tareas del estudio y el nombre de una persona, muestre cuántas tareas pendientes tiene asignadas. Después, haz la prueba de escritorio con estos datos, buscando las de Luis:
| Nº | Título | Responsable | Estado |
|---|---|---|---|
| 1 | Logotipo Panadería Solé | Luis | pendiente |
| 2 | Cartel feria del libro | Nuria | pendiente |
| 3 | Rediseño web Vidal | Luis | completada |
| 4 | Manual de marca Solé | Luis | pendiente |
| 5 | Presupuesto marzo | Marta | pendiente |
Ejercicio 3: Reasignar una tarea
Marta quiere poder reasignar una tarea a otra persona del equipo. Las reglas acordadas son:
- Solo se pueden reasignar tareas pendientes; una completada no se toca.
- El nuevo responsable debe ser Marta, Luis o Nuria.
- No tiene sentido reasignar a la misma persona que ya la tiene: hay que avisar.
Escribe el pseudocódigo de este algoritmo y haz una prueba de escritorio para estos tres casos: (a) reasignar a Nuria una tarea pendiente de Luis, (b) intentar reasignar una tarea completada, (c) reasignar a Luis una tarea que ya es de Luis.
Soluciones
Solución 1.
a) Incumple la finitud. Nada dentro del bucle reduce el número de tareas ni avanza por la lista, así que la condición nunca deja de cumplirse: se imprimiría el mensaje eternamente. Además no tiene salida útil (repite lo mismo sin informar de nada). Corrección:
INICIO
pendientes ← 0
PARA CADA tarea EN lista HACER
SI estado de tarea = "pendiente" ENTONCES
pendientes ← pendientes + 1
FIN SI
FIN PARA
MOSTRAR "Tareas pendientes: " + pendientes
FINb) Incumple la precisión (y, por tanto, el determinismo). «Parece urgente» no es evaluable: dos personas lo interpretarían distinto y la máquina no puede interpretarlo en absoluto. Corrección:
INICIO
LEER prioridad
SI prioridad = "alta" ENTONCES
MOSTRAR "Atender pronto"
SINO
MOSTRAR "Puede esperar"
FIN SI
FINSe ha añadido el SINO para que haya salida en ambos casos.
c) Incumple la salida definida. Calcula correctamente el contador pero no lo muestra: el resultado se pierde al terminar. Corrección: añadir MOSTRAR "Total de tareas: " + contador antes de FIN.
Solución 2.
Pseudocódigo:
INICIO
// Entrada: lista de tareas y nombre de una persona
// Salida: numero de tareas pendientes de esa persona
LEER persona
contador ← 0
PARA CADA tarea EN lista_de_tareas HACER
SI responsable de tarea = persona Y estado de tarea = "pendiente" ENTONCES
contador ← contador + 1
FIN SI
FIN PARA
MOSTRAR persona + " tiene " + contador + " tareas pendientes."
FINDiagrama de flujo:
flowchart TD
A(["INICIO"]) --> B[/"LEER persona"/]
B --> C["contador ← 0"]
C --> D{"Quedan tareas<br/>por revisar?"}
D -->|No| H[/"MOSTRAR persona y contador"/]
D -->|Si| E{"Es de esa persona<br/>Y esta pendiente?"}
E -->|Si| F["contador ← contador + 1"]
E -->|No| G["Pasar a la siguiente"]
F --> G
G --> D
H --> I(["FIN"])
Prueba de escritorio buscando las de Luis:
| Vuelta | Tarea | ¿Es de Luis? | ¿Pendiente? | ¿Cuenta? | contador |
|---|---|---|---|---|---|
| — | (inicio) | — | — | — | 0 |
| 1 | Logotipo Panadería Solé | Sí | Sí | Sí | 1 |
| 2 | Cartel feria del libro | No | Sí | No | 1 |
| 3 | Rediseño web Vidal | Sí | No | No | 1 |
| 4 | Manual de marca Solé | Sí | Sí | Sí | 2 |
| 5 | Presupuesto marzo | No | Sí | No | 2 |
Salida: Luis tiene 2 tareas pendientes. Verificado a ojo: la 1 y la 4. La 3 es de Luis pero está completada, y ese es justamente el caso que comprueba que la condición doble funciona.
Solución 3.
INICIO
// Entrada: una tarea y el nombre del nuevo responsable
// Salida: la tarea reasignada, o un mensaje explicando por que no
LEER tarea
LEER nuevo_responsable
SI estado de tarea = "completada" ENTONCES
MOSTRAR "No se puede reasignar: la tarea ya esta completada."
SINO
SI nuevo_responsable NO ES "Marta" NI "Luis" NI "Nuria" ENTONCES
MOSTRAR "Responsable no valido. Debe ser Marta, Luis o Nuria."
SINO
SI nuevo_responsable = responsable de tarea ENTONCES
MOSTRAR "La tarea ya esta asignada a " + nuevo_responsable
SINO
responsable de tarea ← nuevo_responsable
MOSTRAR "Tarea reasignada a " + nuevo_responsable
FIN SI
FIN SI
FIN SI
FINPrueba de escritorio:
| Caso | Tarea (responsable, estado) | Nuevo responsable | Comprobaciones | Resultado |
|---|---|---|---|---|
| a | Logotipo Solé (Luis, pendiente) | Nuria | No completada → responsable válido → distinto del actual | responsable ← Nuria. «Tarea reasignada a Nuria» |
| b | Rediseño web Vidal (Luis, completada) | Nuria | Completada → se corta en la primera condición | «No se puede reasignar: la tarea ya está completada.» |
| c | Manual de marca (Luis, pendiente) | Luis | No completada → responsable válido → igual al actual | Sin cambios. «La tarea ya está asignada a Luis» |
Observa el orden de las comprobaciones: primero el estado, después la validez del nombre y por último la coincidencia. Ese orden importa. Si comprobásemos primero si coincide el responsable, una tarea completada asignada a Luis que se intentase reasignar a Luis daría el mensaje equivocado. Decidir en qué orden se hacen las comprobaciones es parte del diseño del algoritmo, no un detalle.
Conclusión
Programar empieza lejos del teclado. En esta lección has aprendido que un algoritmo es una secuencia finita de pasos precisos que transforman una entrada en una salida, independiente del lenguaje en que se escriba, y que debe cumplir cinco propiedades: ser preciso, definido, finito, con entrada definida y con salida definida. Has visto cómo descomponer un problema grande en subproblemas manejables, cómo expresar la solución en pseudocódigo con convenciones claras y cómo dibujarla en un diagrama de flujo donde los bucles se ven como flechas que vuelven atrás. Y has practicado la técnica más rentable del oficio: la prueba de escritorio, que con una tabla y un lápiz encuentra errores que en el ordenador costarían horas —como el «ALTA» que no coincidía con «alta».
Todo ello aplicado a TareaFácil: ya tenemos diseñados, verificados y con sus casos límite comprobados los algoritmos de registrar una tarea nueva y de decidir qué tarea atiende Marta primero. No son bocetos: son especificaciones listas para implementar.
Con esto cerramos el módulo 1. Sabes qué es programar, de dónde viene el oficio, cómo se clasifican y eligen los lenguajes, tienes un entorno funcionando con tareafacil.py en tu disco y sabes diseñar un algoritmo antes de escribirlo. Es exactamente el equipaje que hace falta para empezar a escribir código de verdad.
Y ahí vamos ahora. En el módulo 2 dejamos el papel y volvemos a Python con la pieza que resuelve la limitación que arrastramos desde la lección anterior —un programa que solo imprime texto fijo—: las variables, que permiten guardar datos y trabajar con ellos. Empezaremos por Variables y tipos de datos, donde por fin el título, el responsable y la prioridad de una tarea dejarán de estar escritos a mano dentro de un print y pasarán a ser información que el programa maneja.
Fundamentos de la Programación
Módulo 1: Introducción a la Programación
- ¿Qué es la programación?
- Historia de la programación
- Lenguajes de programación
- Entornos de desarrollo
- Del problema al algoritmo
Módulo 2: Conceptos Básicos
- Variables y tipos de datos
- Operadores y expresiones
- Entrada y salida de datos
- Conversión de tipos y validación de datos
Módulo 3: Estructuras de Control
Módulo 4: Funciones y Procedimientos
- Definición y uso de funciones
- Parámetros y retorno de valores
- Ámbito de variables
- Descomponer un programa en funciones
- Funciones como valores: lambda y orden superior
Módulo 5: Estructuras de Datos
- Listas y arreglos
- Cadenas de caracteres
- Diccionarios y conjuntos
- Tuplas y estructuras anidadas
- Guardar datos en archivos: texto, CSV y JSON
Módulo 6: Algoritmos Básicos
Módulo 7: Objetos y Organización del Código
- De los datos a los objetos: clases e instancias
- Atributos, métodos y constructor
- Colecciones de objetos
- Módulos, paquetes e importaciones
Módulo 8: Buenas Prácticas y Herramientas
- Documentación y comentarios
- Depuración y manejo de errores
- Control de versiones
- Pruebas automatizadas
- Estilo, legibilidad y refactorización
