Implementación de un contexto limpio por evaluación, garantizando que cada modificación reevalúe todo desde cero. Se eliminan opciones de menú obsoletas relacionadas con la limpieza de variables y ecuaciones. Se añade la funcionalidad para limpiar el historial de entradas y se actualiza la documentación para reflejar estos cambios.
This commit is contained in:
parent
449d503213
commit
589bab03b2
|
@ -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"
|
|
@ -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`).
|
||||
|
|
|
@ -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)
|
||||
a
|
||||
b
|
|
@ -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,
|
||||
|
|
132
main_calc_app.py
132
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}")
|
||||
|
|
36
readme.md
36
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
|
||||
|
|
Loading…
Reference in New Issue