Función tiene while, autollamada y break ¿es mejorarable?

Colapsar
X
 
  • Filtrar
  • Tiempo
  • Mostrar
Limpiar Todo
nuevos mensajes

  • Función tiene while, autollamada y break ¿es mejorarable?

    Pongo mi conversación con GPT:
    ¿se puede evitar el último if?
    Código:
                // El dibujo puede ser simétrico...
                // a otro ya mostrado. ¿Lo es?
                // Esto puede cambiar:
                DibujoEsSimetrico = "No";
                // Captar valor (colores) de cada fila;
                // si alguna es menor al inverso, sí:
                PunteroDeFilas = 0;
                do {
                    Valor = "";
                    ValorInverso = "";
                    PunteroDeFilas++;
                    PunteroDeColumnas = 0;
                    do {
                        PunteroDeColumnas++;
                        Valor = Valor+eval("Cuadradito"+PunteroDeFilas+"_"+PunteroDeColumnas);
                        ValorInverso = eval("Cuadradito"+PunteroDeFilas+"_"+PunteroDeColumnas)+ValorInverso;
                    } while (PunteroDeColumnas < CuadraditosPorFila);
                    // trace("Valor: "+Valor);
                    // trace("ValorInverso: "+ValorInverso);
                    if (Valor < ValorInverso) {
                        DibujoEsSimetrico = "Sí";
                        break;
                    } else if (Valor > ValorInverso) {
                        // Es ok:
                        break;
                    }
                } while (PunteroDeFilas < CuadraditosPorFila);
                // Este if es probablemente innecesario,
                // porque se supone que el dibujo será simétrico u ok,
                // antes de que se terminen de comparar todas las filas.
                if (DibujoEsSimetrico == "Sí") {
                    // trace ("  El dibujo es simétrico a otro ya mostrado.");
                    DibujosSimetricos++;
                    // Al Puntero ponerlo al final de la fila...
                    // captada como simétrica, para posiblemente...
                    // evitar algunos otros dibujos simétricos:
                    Puntero = CuadraditosPorFila*PunteroDeFilas;
                    Variar();
                } else {
                    DibujosOk++;
                    // trace ("  Mostrando dibujo nro: "+DibujosOk);
                    // Preparar Puntero para generar el próximo dibujo:
                    Puntero = MaxPosiciondePuntero;
                    play ();
                }
    ​
    Parece que la finalidad del código es determinar si un dibujo es simétrico comparando las filas con sus inversas. El bloque del `if` final que mencionas está ligado a cómo se clasifica la simetría o si un dibujo es "ok". Ahora bien, hay varias formas de modificar el código para evitar ese `if` final, dependiendo de cómo se maneje la lógica de control del flujo.

    Una idea clave aquí es que podrías utilizar un enfoque que combine las operaciones dentro del ciclo `do-while` que ya tienes. A continuación, te presento una alternativa que evita el último `if`, incorporando la lógica de manejo de simetría dentro del propio ciclo. Esto se puede hacer eliminando el `DibujoEsSimetrico` y actuando inmediatamente según el resultado de las comparaciones.

    Aquí tienes una versión modificada del código:

    ```javascript
    // El dibujo puede ser simétrico...
    // a otro ya mostrado. ¿Lo es?

    // Captar valor (colores) de cada fila;
    // si alguna es menor al inverso, sí:
    PunteroDeFilas = 0;
    do {
    Valor = "";
    ValorInverso = "";
    PunteroDeFilas++;
    PunteroDeColumnas = 0;
    do {
    PunteroDeColumnas++;
    // Concatenar los valores de la fila y su inverso.
    Valor = Valor + eval("Cuadradito" + PunteroDeFilas + "_" + PunteroDeColumnas);
    ValorInverso = eval("Cuadradito" + PunteroDeFilas + "_" + PunteroDeColumnas) + ValorInverso;
    } while (PunteroDeColumnas < CuadraditosPorFila);

    // Si encontramos que el dibujo es simétrico, actuamos inmediatamente.
    if (Valor < ValorInverso) {
    // El dibujo es simétrico
    DibujosSimetricos++;
    // Poner el puntero al final de la fila simétrica.
    Puntero = CuadraditosPorFila * PunteroDeFilas;
    Variar();
    break;
    } else if (Valor > ValorInverso) {
    // El dibujo es "ok", no es simétrico.
    DibujosOk++;
    // Preparar el puntero para el próximo dibujo.
    Puntero = MaxPosiciondePuntero;
    play();
    break;
    }
    // Si son iguales, seguimos comparando la siguiente fila.

    } while (PunteroDeFilas < CuadraditosPorFila);
    ```

    ### Cambios realizados:
    1. **Eliminación de la variable `DibujoEsSimetrico`**: Ya no es necesario mantener un estado separado para saber si el dibujo es simétrico. En su lugar, la decisión se toma directamente en el ciclo que compara las filas con sus inversas.

    2. **Unificación de la lógica**: En lugar de verificar si el dibujo es simétrico al final del ciclo, se maneja dentro del ciclo. Si `Valor < ValorInverso`, consideramos el dibujo como simétrico y actuamos de inmediato con `DibujosSimetricos++` y el ajuste del puntero. Si `Valor > ValorInverso`, consideramos el dibujo como "ok" y también actuamos inmediatamente. De esta forma, no es necesario un `if` final.

    ### Ventaja:
    Este enfoque hace que el código sea más compacto y evita el uso de la variable `DibujoEsSimetrico` y el `if` final, ya que las decisiones se toman a medida que se comparan las filas durante el ciclo. Esto optimiza el flujo de control del programa y reduce el número de comparaciones innecesarias.​
    ok, pero me faltó decirte un detalle importante: Eso es parte de la función Variar, se autollama, pero luego hay un break, o sea que el while sigue activo hasta que la función termine ¿no? Entonces podría tener muchos while activos. ¿Qué pasa si pongo break y luego llamo a la función? ¿se ejecuta la función o el break hace que lo que esté en el while después de él no se aplique?

    ¡Gracias por la aclaración adicional! Ahora entiendo mejor tu situación. El hecho de que estás trabajando en una función que se autollama (como una especie de recursividad) y que hay un `break` dentro del bucle `while` plantea una situación interesante en cuanto a la ejecución del código.

    ### Respuesta breve:

    Si pones un `break` dentro de un `while`, el `break` inmediatamente detiene la ejecución del ciclo actual y salta **fuera del ciclo**. Cualquier código que esté después del `break` en el mismo bloque de código **no se ejecutará**.

    Por lo tanto, si llamas a la función `Variar()` **después** de un `break`, esa llamada a la función **no se ejecutará** porque el `break` ya habrá terminado el ciclo y salido de él.

    ### Explicación detallada:

    1. **El comportamiento del `break`**:
    - El `break` se utiliza para salir inmediatamente de un bucle (`while`, `for`, etc.).
    - Cuando el `break` es alcanzado, **todo lo que esté después de él dentro del mismo bloque del ciclo no se ejecuta**. El flujo de ejecución salta fuera del bucle.

    Entonces, si tienes algo así:

    ```javascript
    while (condicion) {
    // Código
    if (algunaCondicion) {
    break;
    // Este código nunca se ejecutará:
    Variar();
    }
    }
    ```

    En este caso, la llamada a `Variar()` **no se ejecutará**, ya que el `break` interrumpe el ciclo y sale del `while` inmediatamente.

    2. **¿Qué pasa con la recursividad y el `break`?**
    - Si tu función `Variar()` se autollama y hay un `break` en algún punto antes de esa autollamada, entonces el `while` en el que se encuentra se detendrá antes de que `Variar()` pueda llamarse de nuevo.
    - Por lo tanto, el ciclo actual se detiene, y la ejecución continúa fuera de ese ciclo.

    3. **¿Qué sucede si llamas a `Variar()` antes del `break`?**
    - Si lo que quieres es que la función `Variar()` se ejecute antes de que el ciclo se detenga, debes asegurarte de llamar a `Variar()` **antes** del `break`.

    Por ejemplo:

    ```javascript
    while (condicion) {
    // Código
    if (algunaCondicion) {
    Variar(); // Aquí se llama a la función
    break; // Luego se interrumpe el ciclo
    }
    }
    ```

    En este caso:
    - Primero se llama a `Variar()`.
    - Luego, el `break` detiene el ciclo.

    ### ¿Por qué llamar a `Variar()` después del `break` no funciona?

    El `break` rompe el ciclo, lo que significa que ni siquiera llegas a ejecutar el código después de él dentro del bucle actual. Si quieres ejecutar algo antes de salir del ciclo, como llamar a `Variar()`, debes hacerlo **antes** del `break`.

    ### Ejemplo para tu caso:

    Si tu código se ve más o menos así:

    ```javascript
    do {
    // Comparación de filas
    if (Valor < ValorInverso) {
    // Es simétrico
    DibujosSimetricos++;
    Puntero = CuadraditosPorFila * PunteroDeFilas;
    break; // Aquí se rompe el ciclo, pero Variar() no se llamaría
    Variar(); // Esto no se ejecutará
    } else if (Valor > ValorInverso) {
    // No es simétrico
    DibujosOk++;
    Puntero = MaxPosiciondePuntero;
    break;
    play(); // Lo mismo, no se ejecuta
    }
    } while (PunteroDeFilas < CuadraditosPorFila);
    ```

    En este caso, las llamadas a `Variar()` y `play()` nunca se ejecutarán porque el `break` interrumpe el ciclo antes de llegar a esas líneas.

    Para solucionar esto, puedes reorganizar el código de la siguiente manera:

    ```javascript
    do {
    // Comparación de filas
    if (Valor < ValorInverso) {
    // Es simétrico
    DibujosSimetricos++;
    Puntero = CuadraditosPorFila * PunteroDeFilas;
    Variar(); // Primero llamamos a Variar
    break; // Luego rompemos el ciclo
    } else if (Valor > ValorInverso) {
    // No es simétrico
    DibujosOk++;
    Puntero = MaxPosiciondePuntero;
    play(); // Primero llamamos a play
    break; // Luego rompemos el ciclo
    }
    } while (PunteroDeFilas < CuadraditosPorFila);
    ```

    ### Conclusión:
    - **El `break` detiene el ciclo** inmediatamente, y cualquier código después de él dentro del mismo ciclo no se ejecuta.
    - Si quieres que una función como `Variar()` o `play()` se ejecute antes de salir del ciclo, debes llamar a esa función **antes** del `break`.​
    ¿Pero me conviene entonces quitar el if y que la función se autollame sin haber terminado el while?

    El hecho de que una función se autollame dentro de un ciclo `while` sin haberlo terminado plantea un riesgo de **recursividad infinita** o de **acumulación excesiva de llamadas en la pila de ejecución** (stack overflow), lo cual es algo que generalmente deberías evitar, a menos que haya un manejo cuidadoso de la recursividad.

    ### ¿Qué sucede si llamas a la función sin salir del `while`?
    Si la función se autollama mientras el ciclo `while` original sigue activo, estarás creando **nuevas instancias de la función** en la pila de ejecución antes de que las anteriores terminen. Esto puede generar varios problemas:

    1. **Recursividad no controlada**: Si no hay un caso base claro para terminar las llamadas recursivas, podrías terminar en un ciclo infinito de llamadas a la misma función, lo que agotará la memoria disponible.
    2. **Pila de ejecución**: En JavaScript, cada vez que se llama a una función, se empuja un nuevo contexto de ejecución en la **pila de llamadas** (call stack). Si se llama a una función recursivamente sin terminar el ciclo anterior, la pila se llenará rápidamente, lo que puede provocar un **stack overflow** (desbordamiento de la pila), haciendo que el programa se bloquee.

    ### Alternativa: Evitar la recursividad innecesaria

    En lugar de permitir que la función se autollame sin finalizar el ciclo `while`, una solución más eficiente sería **quitar la recursividad** y manejar todo el flujo a través del ciclo `while`, asegurándote de que el ciclo termine antes de cualquier nueva iteración o llamada a la función. Esto evitará que se acumulen demasiadas llamadas recursivas.

    ### Implementación sin recursividad (sin autollamada)

    Aquí tienes una forma de implementar tu lógica sin usar recursividad. En lugar de que la función se autollame, haces todo desde el ciclo `while`, y utilizas las estructuras de control para manejar el flujo:

    ```javascript
    function Variar() {
    PunteroDeFilas = 0;
    let continuar = true; // Variable para controlar si continuamos el ciclo

    while (PunteroDeFilas < CuadraditosPorFila && continuar) {
    // Inicialización de valores por fila
    Valor = "";
    ValorInverso = "";
    PunteroDeFilas++;
    PunteroDeColumnas = 0;

    // Captar valores de cada fila
    while (PunteroDeColumnas < CuadraditosPorFila) {
    PunteroDeColumnas++;
    Valor = Valor + eval("Cuadradito" + PunteroDeFilas + "_" + PunteroDeColumnas);
    ValorInverso = eval("Cuadradito" + PunteroDeFilas + "_" + PunteroDeColumnas) + ValorInverso;
    }

    // Comparación de la fila con su inversa
    if (Valor < ValorInverso) {
    // Es simétrico
    DibujosSimetricos++;
    Puntero = CuadraditosPorFila * PunteroDeFilas;
    continuar = false; // Detenemos el ciclo
    // Aquí podríamos realizar alguna acción, como llamar a otra función
    // o simplemente terminar la función.
    // Por ejemplo:
    break; // Rompemos el ciclo actual
    } else if (Valor > ValorInverso) {
    // No es simétrico
    DibujosOk++;
    Puntero = MaxPosiciondePuntero;
    continuar = false; // Detenemos el ciclo
    // Aquí podríamos realizar alguna acción, como llamar a otra función
    break; // Rompemos el ciclo
    }
    // Si son iguales, el ciclo sigue con la siguiente fila
    }

    // Aquí termina el ciclo `while`, y la función no se autollama
    // Podrías hacer alguna acción más si es necesario.
    }
    ```

    ### ¿Qué hace este código?

    1. **Ciclo `while` controlado**:
    - El ciclo sigue comparando las filas hasta que encuentra una condición (`Valor < ValorInverso` o `Valor > ValorInverso`). En ese momento, ajusta el puntero y **rompe** el ciclo (`break`).
    - Si ninguna de las dos condiciones se cumple, el ciclo continúa con la siguiente fila.

    2. **Control del ciclo con `continuar`**:
    - Uso de una variable booleana `continuar` para controlar cuándo detener el ciclo si se encuentra una condición que requiere terminar la ejecución.

    3. **Sin recursión**:
    - La función **no se autollama**, lo que evita la acumulación de llamadas en la pila de ejecución.
    - Todo se maneja dentro del ciclo, y se asegura que el ciclo termine de forma controlada.

    ### ¿Cuándo sí deberías usar recursividad?

    Si tu problema tiene una estructura inherentemente recursiva (por ejemplo, si estás navegando un árbol, una estructura de datos o algún algoritmo de búsqueda), la recursión tiene sentido. Sin embargo, en tu caso, dado que ejecutas un ciclo secuencial para comparar filas, la recursión no parece ser la mejor opción.

    ### Conclusión:

    1. **No es recomendable que la función se autollame sin haber terminado el `while`**, ya que esto puede provocar una acumulación innecesaria de llamadas en la pila de ejecución.

    2. **Es preferible eliminar la recursividad** y manejar el flujo de control dentro del ciclo `while`. Esto hará que el código sea más eficiente y evite problemas de desbordamiento de la pila.

    3. **Llama a la función recursivamente solo si es realmente necesario** y si puedes garantizar que la recursividad tenga un caso base claro que la detenga.

    Si sigues este enfoque (sin recursión), te asegurarás de que tu código sea más robusto y eficiente.​
    Parece que hubiera puesto el mismo if de otro modo, o sea... en vez de EsSimetrico creó Continuar, y en vez de poner un if luego del while lo puso dentro, no veo gran diferencia... Código más corto sí, pero más condiciones a cargar cuando chequea si debe terminar el while.

    ¿Se les ocurre algo mejor?
Trabajando...
X