From 589bab03b2f27c052dcc82185cbb7f72c31a10bc Mon Sep 17 00:00:00 2001 From: Miguel Date: Mon, 2 Jun 2025 23:23:52 +0200 Subject: [PATCH] =?UTF-8?q?Implementaci=C3=B3n=20de=20un=20contexto=20limp?= =?UTF-8?q?io=20por=20evaluaci=C3=B3n,=20garantizando=20que=20cada=20modif?= =?UTF-8?q?icaci=C3=B3n=20reeval=C3=BAe=20todo=20desde=20cero.=20Se=20elim?= =?UTF-8?q?inan=20opciones=20de=20men=C3=BA=20obsoletas=20relacionadas=20c?= =?UTF-8?q?on=20la=20limpieza=20de=20variables=20y=20ecuaciones.=20Se=20a?= =?UTF-8?q?=C3=B1ade=20la=20funcionalidad=20para=20limpiar=20el=20historia?= =?UTF-8?q?l=20de=20entradas=20y=20se=20actualiza=20la=20documentaci=C3=B3?= =?UTF-8?q?n=20para=20reflejar=20estos=20cambios.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .doc/CONTEXTO_LIMPIO_SOLUCION.md | 150 +++++++++++++++++++++++++++++++ .doc/Guia_Desarrollo.md | 42 +++++++++ hybrid_calc_history.txt | 28 ++---- hybrid_calc_settings.json | 2 +- main_calc_app.py | 132 ++++----------------------- readme.md | 36 +++++++- 6 files changed, 248 insertions(+), 142 deletions(-) create mode 100644 .doc/CONTEXTO_LIMPIO_SOLUCION.md diff --git a/.doc/CONTEXTO_LIMPIO_SOLUCION.md b/.doc/CONTEXTO_LIMPIO_SOLUCION.md new file mode 100644 index 0000000..e50775a --- /dev/null +++ b/.doc/CONTEXTO_LIMPIO_SOLUCION.md @@ -0,0 +1,150 @@ +# Solución: Contexto Limpio en Cada Evaluación + +## Problema Identificado + +La aplicación tenía un comportamiento inesperado donde al evaluar una secuencia como: +``` +x +x=1 +y+x +x=x+1 +x +``` + +El primer `x` ya mostraba un valor (en este caso `2[Integer]`) cuando debería estar indefinido inicialmente. + +## Causa Raíz REAL + +El problema **NO era solo el historial**, sino que la aplicación mantenía el contexto de variables entre evaluaciones. Cuando el usuario modificaba cualquier línea, la aplicación reevaluaba **manteniendo las variables previamente definidas**, en lugar de construir el contexto desde cero. + +Esto significaba que: +1. Si había `x=1` en alguna línea anterior +2. Y luego el usuario añadía una línea `x` al principio +3. El `x` del principio usaba el valor de la evaluación anterior en lugar de iniciar limpio + +## Solución Implementada + +### Modificación Principal: Contexto Limpio en Cada Evaluación + +**Antes:** +```python +def _evaluate_and_update(self): + """Evalúa todas las líneas y actualiza la salida""" + try: + input_content = self.input_text.get("1.0", tk.END) + if not input_content.strip(): + self._clear_output() + return + + lines = input_content.splitlines() + self._evaluate_lines(lines) # ← Mantenía contexto anterior + + except Exception as e: + self._show_error(f"Error durante evaluación: {e}") +``` + +**Después:** +```python +def _evaluate_and_update(self): + """Evalúa todas las líneas y actualiza la salida""" + try: + input_content = self.input_text.get("1.0", tk.END) + if not input_content.strip(): + self._clear_output() + return + + # NUEVO: Limpiar completamente el contexto antes de cada evaluación + # Esto garantiza que cada modificación reevalúe todo desde cero + self.engine.clear_all() + + lines = input_content.splitlines() + self._evaluate_lines(lines) + + except Exception as e: + self._show_error(f"Error durante evaluación: {e}") +``` + +### Cambios Adicionales + +1. **Eliminación de llamadas redundantes**: Los métodos `clear_variables()`, `clear_equations()` y `clear_all()` ya no llaman a `_evaluate_and_update()` porque ahora cada evaluación limpia automáticamente. + +2. **Limpieza del historial**: También se limpió el historial, pero esto era secundario al problema real. + +3. **Opción manual para limpiar historial**: Se añadió en el menú **Editar → Limpiar historial**. + +## Comportamiento Resultante + +Ahora **cada vez que se modifica cualquier línea**: + +1. **Se limpia completamente el contexto** - `engine.clear_all()` +2. **Se reevalúa TODO desde la línea 1** - construcción paso a paso +3. **El contexto se construye de arriba hacia abajo** - sin memoria de evaluaciones anteriores +4. **Variables solo existen si están definidas en líneas anteriores** - comportamiento predictible + +## Prueba de Verificación + +El script `test_contexto_limpio.py` confirma el comportamiento: + +``` +=== PRUEBA DE CONTEXTO LIMPIO POR EVALUACIÓN === + +--- PRIMERA EVALUACIÓN: x solo --- +Resultado: x # ← Símbolo puro, sin valor +Variables después: {} # ← Contexto limpio + +--- TERCERA EVALUACIÓN: x, x=1, y+x --- +x (antes de asignar): x # ← Siempre símbolo puro al inicio +x=1: 1 # ← Se asigna +y+x: y + 1 # ← Usa el x=1 asignado arriba +Variables después: {'x': 1} # ← Contexto construido paso a paso +``` + +## Beneficios de la Solución Real + +- ✅ **Contexto predecible**: Cada evaluación inicia completamente limpia +- ✅ **Construcción desde arriba**: Las variables solo existen si se definen arriba +- ✅ **Sin memoria persistente**: No hay variables "fantasma" de evaluaciones anteriores +- ✅ **Comportamiento intuitivo**: Lo que ves es lo que tienes +- ✅ **Reevaluación completa**: Cada cambio reconstruye todo desde cero + +## Archivos Modificados + +1. `main_calc_app.py` - Método `_evaluate_and_update()` con limpieza automática +2. `main_calc_app.py` - Métodos `clear_*()` simplificados +3. `main_calc_app.py` - **Menús simplificados**: Eliminadas opciones redundantes +4. `hybrid_calc_history.txt` - Limpiado (cambio menor) +5. `test_contexto_limpio.py` - Script de prueba (nuevo) + +### Cambios en la Interfaz de Usuario + +**Opciones ELIMINADAS del menú (ya no tienen sentido):** + +- **Menú Editar**: + - ❌ "Limpiar variables" + - ❌ "Limpiar ecuaciones" + - ❌ "Limpiar todo" + +- **Menú CAS completo**: + - ❌ "Mostrar variables" + - ❌ "Mostrar ecuaciones" + - ❌ "Resolver sistema" + +**Opciones CONSERVADAS (aún útiles):** + +- **Menú Editar**: + - ✅ "Limpiar entrada" - limpia el editor de texto + - ✅ "Limpiar salida" - limpia el panel de resultados + - ✅ "Limpiar historial" - elimina el archivo de historial + +- **Menú Archivo**: + - ✅ "Nuevo" - inicia sesión nueva (limpia entrada y salida) + - ✅ "Cargar..." / "Guardar como..." - manejo de archivos + +**La solución real no era el historial, sino garantizar que cada evaluación construye el contexto completamente desde cero.** + +## Impacto en la Experiencia de Usuario + +**✅ Interfaz más limpia**: Sin opciones confusas que no funcionan como se espera +**✅ Comportamiento predecible**: Lo que ves en el editor es exactamente lo que tienes +**✅ Simplicidad**: Menos opciones que manejar, enfoque en la funcionalidad principal +**✅ Consistencia**: El comportamiento es coherente con la filosofía "de arriba hacia abajo" \ No newline at end of file diff --git a/.doc/Guia_Desarrollo.md b/.doc/Guia_Desarrollo.md index e591038..68d7cec 100644 --- a/.doc/Guia_Desarrollo.md +++ b/.doc/Guia_Desarrollo.md @@ -9,6 +9,7 @@ Este documento describe el estado actual y los objetivos de desarrollo para la * - Integrar todas las funciones de SymPy directamente - SymPy maneja toda la evaluación algebraica y ecuaciones - Cada linea de ingreso corresponde a una sola linea de resultados +- **CONTEXTO LIMPIO POR EVALUACIÓN**: Cada vez que se hace una evaluación se comienza con el contexto completamente limpio y se evalúa desde arriba hacia abajo, línea por línea. Esto garantiza comportamiento predecible sin "memoria" de evaluaciones anteriores. - La interfaz de usuario se divide en 2 columnas, a la izquierda el area de ingreso de datos y equaciones a ser evaluadas, a la derecha el area de resultados que se colorean segun cada tipo de respuesta. ### 2. **Sintaxis Simplificada con Corchetes (Única)** @@ -454,6 +455,47 @@ else: - **eval/exec opcional**: Solo disponible con sintaxis especial (ej: `@eval: código_python`) - **Contexto unificado**: Un solo namespace SymPy para toda la sesión +### **Comportamiento de Contexto Limpio (IMPLEMENTADO)** + +**Principio fundamental**: Cada modificación del usuario resulta en una evaluación completa desde cero. + +#### Implementación +- **Limpieza automática**: Cada evaluación inicia con `engine.clear_all()` +- **Construcción incremental**: El contexto se construye línea por línea, de arriba hacia abajo +- **Sin persistencia**: No hay variables "fantasma" de evaluaciones anteriores +- **Predictibilidad total**: El primer `x` en cualquier secuencia siempre es un símbolo puro +- **Evaluación inicial del historial**: Al cargar la aplicación, se evalúa el historial una vez para mostrar resultados + +#### Ejemplo de Comportamiento +```python +# Primera evaluación: solo "x" +x # → Symbol('x') puro, sin valor + +# Segunda evaluación: "x, x=1" +x # → Symbol('x') puro (contexto limpio) +x=1 # → Asigna x=1 + +# Tercera evaluación: "x, x=1, y+x" +x # → Symbol('x') puro (contexto limpio) +x=1 # → Asigna x=1 +y+x # → y + 1 (usa x=1 definido arriba) +``` + +#### Implicaciones para el Desarrollo +- **No necesidad de gestión manual de contexto**: El sistema lo maneja automáticamente +- **Comportamiento determinista**: Misma entrada → mismo resultado, siempre +- **Simplicidad de debugging**: El estado siempre es predecible +- **Eliminación de opciones de menú obsoletas**: "Limpiar variables/ecuaciones" no tienen sentido + +#### Cambios en la Interfaz de Usuario +**Opciones ELIMINADAS (redundantes):** +- Menú CAS completo (variables, ecuaciones, resolver sistema) +- "Limpiar variables", "Limpiar ecuaciones", "Limpiar todo" + +**Opciones CONSERVADAS (útiles):** +- "Limpiar entrada/salida" (afecta interfaz visual) +- "Limpiar historial" (afecta archivo persistente) + ## Métricas de Éxito - [X] Sintaxis `Class[args]` funciona consistentemente (`bracket_parser.py`, `hybrid_base_types.py`). diff --git a/hybrid_calc_history.txt b/hybrid_calc_history.txt index 8c9fd9c..adb71ff 100644 --- a/hybrid_calc_history.txt +++ b/hybrid_calc_history.txt @@ -1,26 +1,10 @@ -Hex[FF] -ip=IP4[110.1.30.70/24] - -ip.NetworkAddress() -500/25 +x=12 +a=x -Dec[Hex[ff]] +x=2 +b=x -Hex[ff] - -Hex[ff].toDecimal() - -n=IP4[110.1.30.70;255.255.255.0] - -n.mask() - -m=IP4Mask[23] - -IP4Mask[22].to_hex() - -n=IP4[110.1.30.70;255.255.255.0] - -latex[x**z/rr] -latex(x**z/rr) \ No newline at end of file +a +b \ No newline at end of file diff --git a/hybrid_calc_settings.json b/hybrid_calc_settings.json index d0c3bf5..8bb3cfb 100644 --- a/hybrid_calc_settings.json +++ b/hybrid_calc_settings.json @@ -1,5 +1,5 @@ { - "window_geometry": "1020x700+238+107", + "window_geometry": "1020x700+144+161", "sash_pos_x": 355, "symbolic_mode": true, "show_numeric_approximation": true, diff --git a/main_calc_app.py b/main_calc_app.py index a3b4ea2..0972f77 100644 --- a/main_calc_app.py +++ b/main_calc_app.py @@ -339,17 +339,7 @@ CLASES DISPONIBLES: edit_menu.add_command(label="Limpiar entrada", command=self.clear_input) edit_menu.add_command(label="Limpiar salida", command=self.clear_output) edit_menu.add_separator() - edit_menu.add_command(label="Limpiar variables", command=self.clear_variables) - edit_menu.add_command(label="Limpiar ecuaciones", command=self.clear_equations) - edit_menu.add_command(label="Limpiar todo", command=self.clear_all) - - # Menú CAS - cas_menu = Menu(menubar, tearoff=0) - menubar.add_cascade(label="CAS", menu=cas_menu) - cas_menu.add_command(label="Mostrar variables", command=self.show_variables) - cas_menu.add_command(label="Mostrar ecuaciones", command=self.show_equations) - cas_menu.add_separator() - cas_menu.add_command(label="Resolver sistema", command=self.solve_system) + edit_menu.add_command(label="Limpiar historial", command=self.clear_history) # Menú Configuración config_menu = Menu(menubar, tearoff=0, bg="#3c3c3c", fg="white") @@ -690,6 +680,10 @@ CLASES DISPONIBLES: self._clear_output() return + # NUEVO: Limpiar completamente el contexto antes de cada evaluación + # Esto garantiza que cada modificación reevalúe todo desde cero + self.engine.clear_all() + lines = input_content.splitlines() self._evaluate_lines(lines) @@ -920,7 +914,6 @@ CLASES DISPONIBLES: """Inicia nueva sesión""" self.clear_input() self.clear_output() - self.engine.clear_all() def load_file(self): """Carga archivo en el editor""" @@ -977,20 +970,14 @@ CLASES DISPONIBLES: """Limpia panel de salida""" self._clear_output() - def clear_variables(self): - """Limpia variables del motor""" - self.engine.clear_variables() - self._evaluate_and_update() - - def clear_equations(self): - """Limpia ecuaciones del motor""" - self.engine.clear_equations() - self._evaluate_and_update() - - def clear_all(self): - """Limpia variables y ecuaciones""" - self.engine.clear_all() - self._evaluate_and_update() + def clear_history(self): + """Limpia el archivo de historial""" + try: + if os.path.exists(self.HISTORY_FILE): + os.remove(self.HISTORY_FILE) + messagebox.showinfo("Éxito", "Historial limpiado correctamente.") + except Exception as e: + messagebox.showerror("Error", f"No se pudo limpiar el historial:\n{e}") def copy_output(self): """Copia el contenido de la salida al portapapeles""" @@ -999,95 +986,6 @@ CLASES DISPONIBLES: self.root.clipboard_clear() self.root.clipboard_append(content) - def show_variables(self): - """Muestra ventana con variables definidas""" - variables = self.engine.symbol_table - - window = tk.Toplevel(self.root) - window.title("Variables Definidas") - window.geometry("500x400") - window.configure(bg="#2b2b2b") - - text_widget = scrolledtext.ScrolledText( - window, - font=("Consolas", 11), - bg="#1e1e1e", - fg="#d4d4d4" - ) - text_widget.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) - - if variables: - content = "Variables definidas:\n\n" - for name, value in variables.items(): - content += f"{name} = {value}\n" - else: - content = "No hay variables definidas." - - text_widget.insert("1.0", content) - text_widget.config(state="disabled") - - def show_equations(self): - """Muestra ventana con ecuaciones definidas""" - equations = self.engine.equations - - window = tk.Toplevel(self.root) - window.title("Ecuaciones Definidas") - window.geometry("500x400") - window.configure(bg="#2b2b2b") - - text_widget = scrolledtext.ScrolledText( - window, - font=("Consolas", 11), - bg="#1e1e1e", - fg="#d4d4d4" - ) - text_widget.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) - - if equations: - content = "Ecuaciones en el sistema:\n\n" - for i, eq in enumerate(equations, 1): - content += f"{i}. {eq}\n" - else: - content = "No hay ecuaciones en el sistema." - - text_widget.insert("1.0", content) - text_widget.config(state="disabled") - - def solve_system(self): - """Resuelve el sistema de ecuaciones""" - try: - if not self.engine.equations: - messagebox.showinfo("Info", "No hay ecuaciones para resolver.") - return - - solutions = self.engine.solve_system() - - window = tk.Toplevel(self.root) - window.title("Soluciones del Sistema") - window.geometry("500x400") - window.configure(bg="#2b2b2b") - - text_widget = scrolledtext.ScrolledText( - window, - font=("Consolas", 11), - bg="#1e1e1e", - fg="#d4d4d4" - ) - text_widget.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) - - content = "Soluciones del sistema:\n\n" - if isinstance(solutions, dict): - for var, value in solutions.items(): - content += f"{var} = {value}\n" - else: - content += str(solutions) - - text_widget.insert("1.0", content) - text_widget.config(state="disabled") - - except Exception as e: - messagebox.showerror("Error", f"Error resolviendo sistema:\n{e}") - def show_types_syntax(self): """Muestra sintaxis de tipos disponibles - NUEVA FUNCIÓN""" try: @@ -1295,7 +1193,7 @@ programación y análisis numérico. text_widget.config(state="disabled") def load_history(self): - """Carga historial de entrada""" + """Carga historial de entrada y realiza evaluación inicial""" try: if os.path.exists(self.HISTORY_FILE): with open(self.HISTORY_FILE, "r", encoding="utf-8") as f: @@ -1303,6 +1201,8 @@ programación y análisis numérico. if content.strip(): self.input_text.insert("1.0", content) + # Hacer evaluación inicial para mostrar resultados del historial + # Esto mantiene el comportamiento de contexto limpio pero muestra resultados self.root.after_idle(self._evaluate_and_update) except Exception as e: print(f"Error cargando historial: {e}") diff --git a/readme.md b/readme.md index e08381b..8b6bd1a 100644 --- a/readme.md +++ b/readme.md @@ -10,6 +10,7 @@ Sistema de Álgebra Computacional (CAS) que combina SymPy con clases especializa - **Sistema de tipos dinámico**: Auto-descubrimiento desde `custom_types/` - **Sintaxis simplificada**: `Tipo[args]` en lugar de `Tipo("args")` - **Detección automática de ecuaciones**: Sin sintaxis especial +- **Contexto limpio por evaluación**: Cada modificación evalúa todo desde cero, garantizando comportamiento predecible - **Resultados interactivos**: Elementos clickeables para plots, matrices y listas - **Autocompletado inteligente**: Basado en tipos registrados dinámicamente @@ -106,6 +107,33 @@ y = x + 2 # y es 7 (evaluado) z = x + a # z es Symbol('x') + Symbol('a') (simbólico) ``` +### Comportamiento de Contexto Limpio + +**Principio fundamental**: Cada vez que modifica cualquier línea, la aplicación reevalúa **todo** desde cero. + +#### Ventajas +- **Predecible**: El primer `x` en cualquier secuencia siempre es un símbolo puro +- **Sin sorpresas**: No hay variables "fantasma" de evaluaciones anteriores +- **Contextual**: Las variables solo existen si están definidas en líneas anteriores +- **Determinista**: Misma entrada → mismo resultado, siempre + +#### Ejemplo +```python +# Al escribir esta secuencia línea por línea: +x # → Symbol('x') puro (sin valor) +x=1 # → Asigna x=1 +y+x # → y + 1 (usa x=1 definido arriba) +x=x+1 # → Incrementa x a 2 +x # → Muestra 2 +``` + +**Importante**: Cada modificación reconstruye el contexto desde la línea 1 hacia abajo. + +#### Carga de Historial +- Al abrir la aplicación, se carga el historial anterior y se evalúa una vez para mostrar resultados +- A partir de ese momento, cada modificación limpia el contexto y reevalúa todo desde cero +- El historial se guarda automáticamente al cerrar la aplicación + ## Interfaz de Usuario ### Paneles @@ -119,11 +147,13 @@ z = x + a # z es Symbol('x') + Symbol('a') (simbólico) ### Menús - **Archivo**: Nuevo, Cargar, Guardar -- **Editar**: Limpiar, operaciones CAS -- **CAS**: Variables, ecuaciones, resolver sistema -- **Tipos**: Información de tipos, recargar sistema +- **Editar**: Limpiar entrada/salida, Limpiar historial +- **Configuración**: Modos simbólicos, Recargar tipos personalizados +- **Tipos**: Información de tipos, Sintaxis de tipos - **Ayuda**: Guías y referencia +**Nota**: Se han eliminado las opciones relacionadas con limpiar variables/ecuaciones ya que el contexto se limpia automáticamente en cada evaluación. + ## Sistema de Tipos Dinámico ### Auto-descubrimiento