import streamlit as st import pandas as pd import itertools import re # --- Eigener LaTeX-Generator für die Schaltfunktion --- def to_custom_latex(expr_str): """ Wandelt den bereinigten Python-Logik-String in sauberen LaTeX-Code mit \overline{...} für NOT, \oplus für XOR, \wedge für AND und \vee für OR um. """ # Temporäre Platzhalter für geklammerte Negationen oder Variablen-Negationen # Findet Ausdrücke wie ~(A) oder ~A und ersetzt sie durch \overline{...} # Regulärer Ausdruck fängt Ausdrücke mit Tilde ab # 1. Negationen mit Klammern: ~(A + B) -> \overline{A + B} while "~(" in expr_str: # Einfacher Parser für passende Klammern start_idx = expr_str.find("~(") open_brackets = 0 end_idx = -1 for i in range(start_idx + 1, len(expr_str)): if expr_str[i] == "(": open_brackets += 1 elif expr_str[i] == ")": open_brackets -= 1 if open_brackets == 0: end_idx = i break if end_idx != -1: inner = expr_str[start_idx+2:end_idx] # Rekursiver Aufruf für das Innere der Klammer expr_str = expr_str[:start_idx] + f"\\overline{{{to_custom_latex(inner)}}}" + expr_str[end_idx+1:] else: break # 2. Einfache Variablen-Negation: ~A -> \overline{A} expr_str = re.sub(r'~([a-zA-Z])', r'\\overline{\1}', expr_str) # 3. Restliche mathematische Symbole ersetzen expr_str = expr_str.replace("&", r" \wedge ") expr_str = expr_str.replace("|", r" \vee ") expr_str = expr_str.replace("^", r" \oplus ") return expr_str # --- Streamlit UI --- st.set_page_config(page_title="Wahrheitstabelle & Logik-Parser", layout="wide") st.title("🎛️ Dynamischer Wahrheitstabellen-Generator") st.markdown(""" Geben Sie eine Schaltfunktion ein. Folgende Operatoren sind definiert: * **`*`** : UND ($\land$) * **`+`** : ODER ($\lor$) * **`^`** : EXKLUSIV-ODER ($\oplus$) * **`!`** oder **`~`** : NEGATION ($\overline{A}$) """) # Layout: 2 Spalten col1, col2 = col1, col2 = st.columns([1, 2]) with col1: st.subheader("Eingabe & Konfiguration") # Standard-Beispiel user_input = st.text_input("Schaltfunktion f:", value="(A ^ B) * !C") if user_input: # Eingabe bereinigen und in valides Python-Boole-Format bringen clean_expr = user_input.replace(" ", "") clean_expr = clean_expr.replace("+", " | ") clean_expr = clean_expr.replace("*", " & ") clean_expr = clean_expr.replace("!", "~") # Erkannte Variablen extrahieren detected_vars = sorted(list(set(re.findall(r'[a-zA-Z]', clean_expr)))) if not detected_vars: st.error("Keine Variablen (Buchstaben) in der Funktion gefunden.") st.stop() st.success("Funktion erfolgreich interpretiert!") # --- Dynamische Sortierung der Spalten --- st.markdown("### Spaltenreihenfolge") ordered_vars = st.multiselect( "Reihenfolge der Variablen festlegen:", options=detected_vars, default=detected_vars ) # Validierung der Auswahl missing_vars = set(detected_vars) - set(ordered_vars) if missing_vars: st.warning(f"Bitte alle Variablen auswählen. Es fehlen noch: {', '.join(missing_vars)}") st.stop() with col2: st.subheader("Darstellung & Wahrheitstabelle") if user_input and detected_vars: # --- LaTeX Darstellung (Eigener Parser ohne SymPy-Winkel-Zwang) --- st.markdown("#### Logische Formel (LaTeX)") latex_formula = to_custom_latex(clean_expr) st.latex(f"f = {latex_formula}") # --- Wahrheitstabelle generieren via Python eval() --- st.markdown("#### Wahrheitstabelle") num_vars = len(ordered_vars) combinations = list(itertools.product([0, 1], repeat=num_vars)) table_data = [] for combo in combinations: # Zustand für die aktuelle Zeile als Dictionary (z.B. {'A': 0, 'B': 1}) var_values = dict(zip(ordered_vars, combo)) # Python-konformen String für die Auswertung vorbereiten. # Da Python mit '&', '|', '~', '^' nativ auf Integers (0 und 1) bitweise rechnet, # können wir den String direkt evaluieren, wenn wir die Variablen ersetzen. eval_expr = clean_expr for var, val in var_values.items(): # Nutze Regex-Word-Boundaries, damit 'A' nicht in 'and' oder längeren Ausdrücken ersetzt wird eval_expr = re.sub(r'\b' + var + r'\b', str(val), eval_expr) try: # Berechne das Ergebnis (0 oder 1). # Das bitweise NOT (~) von 0 ist in Python -1, von 1 ist es -2. # Daher maskieren wir das Ergebnis am Ende mit & 1, um immer 0 oder 1 zu erhalten. result = eval(eval_expr) & 1 except Exception as e: st.error(f"Fehler bei der Tabellenberechnung: {e}") st.stop() row = list(combo) + [result] table_data.append(row) # DataFrame erstellen column_names = ordered_vars + ["f"] df = pd.DataFrame(table_data, columns=column_names) # Tabelle anzeigen st.dataframe( df.style.format(hyperlinks="to_html") .background_gradient(subset=["f"], cmap="Blues", low=0.1, high=0.05), use_container_width=True, hide_index=True )