Saltar a contenido

Práctica 2.4: Depurar programas

P2.4 - Depuración del algoritmo burbuja

En esta práctica vas a investigar un error de lógica en un programa que ordena una lista mediante el algoritmo burbuja. Primero reproducirás el comportamiento incorrecto, después formularás una hipótesis y utilizarás el depurador para observar i, j y la lista en cada comparación. Por último, corregirás el programa y demostrarás que funciona con datos normales, repetidos y límite.

El resultado esperado no es solo una lista ordenada: también debes poder explicar qué observaste, qué hipótesis comprobaste y por qué la corrección resuelve el problema.

Recorrido del algoritmo burbuja
El algoritmo compara elementos adyacentes y desplaza los valores mayores hacia el final.

1. Objetivos

  • Explicar el funcionamiento de dos bucles anidados que comparan elementos adyacentes.
  • Reproducir un error de lógica con una entrada conocida y diferenciar el resultado obtenido del resultado esperado.
  • Formular una hipótesis comprobable antes de modificar el código.
  • Colocar puntos de interrupción, ejecutar paso a paso e inspeccionar variables en Visual Studio Code.
  • Corregir el algoritmo y verificarlo con listas desordenadas, ordenadas, repetidas, vacías y de un solo elemento.
  • Documentar las pruebas y las evidencias de depuración para que otra persona pueda repetirlas.

2. Relación con RA y criterios de evaluación

Código Descripción literal de la normativa
RA 3 Escribe y depura código, analizando y utilizando las estructuras de control del lenguaje.
CE 3.f Se han probado y depurado los programas.

Las evidencias se concretan así:

  • CE 3.f: los ejercicios 2.4.1 y 2.4.2 incluyen la reproducción del fallo, la hipótesis, la observación paso a paso, la corrección y las pruebas de casos normales y frontera.
  • El README.md del ejercicio 2.4.3 conserva los resultados esperados y obtenidos, la explicación de la corrección y las dos evidencias de depuración.

3. Requisitos y recursos

  • Python 3.9 o posterior, un editor y una terminal para ejecutar los programas.
  • Visual Studio Code con la extensión Python de Microsoft. Si utilizas otro IDE, emplea sus controles equivalentes y conserva las mismas evidencias.
  • Conocimientos de listas, for, range, condiciones, funciones y copia de listas.
  • Lectura previa de la teoría de sentencias iterativas y de salto.
  • Lectura de la teoría de depuración de programas, especialmente los apartados sobre hipótesis, breakpoints, Variables y ejecución paso a paso.
  • La presentación de depuración como repaso visual.

La práctica utiliza una lista pequeña para que puedas calcular a mano el resultado de cada comparación. No uses list.sort() ni sorted() para ordenar la lista del algoritmo: sorted() se reservará para una aserción de desarrollo que compruebe el resultado final.

4. Pasos a seguir

4.1. Preparar el trabajo y reproducir el problema

  1. Crea una carpeta de trabajo llamada practica_u2_4_depurar_burbuja con los archivos ejercicio2.4.1.py, ejercicio2.4.2.py y README.md.

  2. Copia el siguiente programa inicial en ejercicio2.4.1.py. El error está intencionado y es de lógica, no de sintaxis.

    def ordenar_burbuja(valores: list[int]) -> list[int]:
        """Devuelve una copia de la lista ordenada mediante burbuja."""
        lista = valores.copy()
    
        for i in range(len(lista) - 1):
            for j in range(len(lista) - 1 - i):
                if lista[j] < lista[j + 1]:
                    lista[j], lista[j + 1] = lista[j + 1], lista[j]
    
        return lista
    
    
    datos = [8, 3, 1, 19, 14]
    resultado = ordenar_burbuja(datos)
    print("Entrada:", datos)
    print("Resultado:", resultado)
    
  3. Ejecuta el archivo desde la terminal:

    python ejercicio2.4.1.py
    
  4. Anota en el README.md el resultado obtenido y el resultado esperado. Para [8, 3, 1, 19, 14], la salida esperada en orden ascendente es [1, 3, 8, 14, 19]. No cambies todavía el código.

4.2. Formular una hipótesis

  1. Describe en el README.md la diferencia entre el resultado obtenido y el esperado. Indica si el problema es de sintaxis, de ejecución o de lógica, y justifica la respuesta.

  2. Escribe una hipótesis concreta antes de abrir el depurador. Por ejemplo: «La condición del if intercambia los valores en el orden contrario al que necesita una ordenación ascendente». Puedes formular otra hipótesis, pero debe poder comprobarse observando lista[j] y lista[j + 1].

  3. Coloca un punto de interrupción en la línea del if haciendo clic en el margen izquierdo o pulsando F9. Inicia la depuración con Run and Debug y F5.

4.3. Observar el estado paso a paso

  1. Cuando la ejecución se detenga, comprueba en Variables los valores de i, j y lista. Recuerda que la línea resaltada es la próxima instrucción que se ejecutará, no una instrucción ya ejecutada.

  2. Pulsa F10 para avanzar sin entrar en una función y observa qué ocurre después de cada comparación. Añade lista, i y j al panel Watch si necesitas mantenerlos visibles.

  3. En la primera comparación, lista[j] vale 8 y lista[j + 1] vale 3. Comprueba si la condición permite el intercambio que necesita el orden ascendente. Sigue al menos una comparación en la que sí se intercambien los valores y anota el estado anterior y posterior de la lista.

  4. Cambia únicamente la condición que has investigado para que se intercambien los elementos cuando el de la izquierda sea mayor que el de la derecha. Ejecuta de nuevo el programa y comprueba que la salida coincide con la esperada.

  5. Guarda en el README.md una evidencia de la sesión: una captura en la que se vea el punto de interrupción y otra en la que se distingan los valores de i, j y lista. Si una sola captura contiene ambas evidencias, explica qué representa cada elemento.

4.4. Mejorar y verificar la implementación

  1. Copia el algoritmo corregido en ejercicio2.4.2.py y añade una variable hubo_intercambio para detener los recorridos cuando una pasada completa no haya cambiado la lista. Inicializa esa variable al principio de cada pasada.

  2. Coloca la asignación hubo_intercambio = True dentro del intercambio y usa esa variable en la condición de continuación del bucle exterior. Comprueba con el depurador que una lista ya ordenada necesita menos pasadas.

  3. Añade una aserción de desarrollo después de llamar a la función. La aserción compara el resultado del algoritmo con una referencia, pero no sustituye al algoritmo que estás practicando.

    datos = [8, 3, 1, 19, 14]
    resultado = ordenar_burbuja(datos)
    assert resultado == sorted(datos)
    
  4. Prueba, como mínimo, estos casos y anota la salida esperada antes de ejecutarlos: [8, 3, 1, 19, 14], [1, 2, 3, 4], [4, 3, 2, 1], [3, 1, 3, 2], [] y [7].

  5. Comprueba que la función devuelve una copia ordenada y que la lista original no cambia. Si la aserción falla, vuelve al depurador y observa el primer estado en el que el resultado deja de coincidir con tu hipótesis.

4.5. Documentar las comprobaciones finales

  1. Completa el README.md con tu nombre o identificador, el entorno utilizado, la hipótesis inicial, los casos de prueba, los resultados obtenidos y las decisiones tomadas durante la depuración.

  2. Incluye dos evidencias de depuración: un punto de interrupción activo y una inspección de variables durante una comparación. No incluyas datos personales ni información sensible en las capturas.

  3. Retira cualquier print() o breakpoint() temporal que hayas añadido para investigar. El programa final debe conservar solo las salidas necesarias y la aserción de desarrollo solicitada.

  4. Ejecuta de nuevo los dos archivos desde la terminal y copia en el README.md una salida final que demuestre que el caso principal termina correctamente.

5. Comprobaciones de finalización

  • ejercicio2.4.1.py ordena [8, 3, 1, 19, 14] como [1, 3, 8, 14, 19].
  • ejercicio2.4.2.py utiliza dos bucles, reduce el rango de comparaciones en cada pasada y puede terminar antes cuando no hay intercambios.
  • Los dos programas se ejecutan sin errores de sintaxis ni excepciones de índice.
  • Se han comprobado una lista desordenada, una ordenada, una invertida, una con duplicados, una vacía y otra con un solo elemento.
  • La lista original no se modifica porque la función trabaja sobre una copia.
  • La aserción de desarrollo pasa y se entiende que sirve para comprobar el código durante el desarrollo, no para validar una entrada externa.
  • El README.md contiene la hipótesis, los resultados esperados y obtenidos, las decisiones de depuración y las dos evidencias solicitadas.
  • El código no conserva breakpoint() ni mensajes temporales de investigación.

6. Ejercicios y entregables

  1. Ejercicio 2.4.1 — Corregir el orden. Depura el programa inicial, demuestra que produce un orden incorrecto, localiza la comparación que causa el problema y corrígela sin modificar el resto del algoritmo hasta haber comprobado la hipótesis.
  2. Ejercicio 2.4.2 — Evitar pasadas innecesarias. Implementa la variante que usa hubo_intercambio, depúrala con una lista ya ordenada y añade la aserción de desarrollo indicada.
  3. Ejercicio 2.4.3 — Informe de depuración. Completa el README.md con una tabla o lista de casos de prueba, la hipótesis, la explicación de la corrección y las evidencias de la sesión. Relaciona al menos una observación de i, j y lista con el resultado final.
  4. Entregables. Entrega ejercicio2.4.1.py, ejercicio2.4.2.py y README.md. El README debe incluir tu nombre o identificador, el entorno, los casos probados, los resultados esperados y obtenidos, la hipótesis, la explicación de la solución, la aserción utilizada y las dos evidencias de depuración.

7. Solución orientativa

En el primer ejercicio, una ordenación ascendente solo debe intercambiar dos valores adyacentes cuando el valor de la izquierda sea mayor que el de la derecha. La condición inicial usaba el signo contrario, por eso desplazaba los valores mayores hacia el principio y producía un orden descendente.

Una posible solución para el segundo ejercicio es la siguiente:

def ordenar_burbuja(valores: list[int]) -> list[int]:
    """Devuelve una copia de valores ordenada de menor a mayor."""
    lista = valores.copy()

    i = 0
    hubo_intercambio = True
    while i < len(lista) - 1 and hubo_intercambio:
        hubo_intercambio = False

        for j in range(len(lista) - 1 - i):
            if lista[j] > lista[j + 1]:
                lista[j], lista[j + 1] = lista[j + 1], lista[j]
                hubo_intercambio = True

        i += 1

    return lista


datos = [8, 3, 1, 19, 14]
resultado = ordenar_burbuja(datos)
assert resultado == sorted(datos)
print("Entrada:", datos)
print("Resultado:", resultado)

El resultado esperado de este ejemplo es:

Entrada: [8, 3, 1, 19, 14]
Resultado: [1, 3, 8, 14, 19]

No se exige copiar esta implementación literalmente. Para contrastar la solución, comprueba que la comparación, el límite len(lista) - 1 - i, el intercambio, la condición de parada temprana y la copia de la lista cumplen su propósito. Una AssertionError debe llevarte a revisar el estado observado, no a eliminar la aserción sin investigar la causa.

8. Fuentes y referencias