Aussagenlogik in Java Programmieren

MJannek

Bekanntes Mitglied
Hi Leute, ich suche nach einem Ansatz, um aussagenlogische Formeln in Java auszuwerten. Ich möchte gerne eine aussagenlogische Formel eingeben und eine wahrheitswertetabelle (mit allen Zwischenschritten) ausgegeben bekommen. Hat jemand eine Idee? Vielen Dank im Voraus.

Markus
 
Du musst ein wenig konkreter werden: was genau willst Du eingeben/auswerten? Terme in der Art: NOT X AND B -> zeig mir mal die Wahrheitstabelle? Und als Ergebnis dann etwas wie:

XBNOT XBNOT X AND B
FALSEFALSETRUEFALSEFALSE
FALSETRUETRUETRUETRUE
TRUEFALSEFALSEFALSEFALSE
TRUETRUEFALSETRUEFALSE
 
Du musst ein wenig konkreter werden: was genau willst Du eingeben/auswerten? Terme in der Art: NOT X AND B -> zeig mir mal die Wahrheitstabelle? Und als Ergebnis dann etwas wie:

XBNOT XBNOT X AND B
FALSEFALSETRUEFALSEFALSE
FALSETRUETRUETRUETRUE
TRUEFALSEFALSEFALSEFALSE
TRUETRUEFALSETRUEFALSE
Bsp.:

I: Not X And B (Operatoren als Symbole)
O: Tabelle
 
I: Not X And B (Operatoren als Symbole)
Du willst die Symbole eintippen lassen?

Nachtrag: Wie auch immer, im Endeffekt modellierst Du diese Ausdrücke mit Objekten, die ggf. das Ergebnis des Parsens sind, ziehst Dir die Menge der Variablen raus, gehst alle Kombinationen durch, lässt die Ausdrücke je Kombination auswerten und gibst für jede Kombination eine Zeile der Tabelle aus.
 
Quick & Dirty:

Ansatz von https://stackoverflow.com/a/26227947/19657183 übernommen.

Java:
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Predicate;
import java.util.function.BiPredicate;

public class LogicalTerm {
    public static interface Expression {
        boolean eval();
    }
    public static class BinaryExpression implements Expression {
        private final Expression left;
        private final Expression right;
        private final char operator;
        private final BiPredicate<Expression, Expression> pred;
        BinaryExpression(Expression left, Expression right, char operator, BiPredicate<Expression, Expression> pred) {
            this.operator = operator;
            this.left = left;
            this.right = right;
            this.pred = pred;
        }
        public Expression getLeft() { return left; }
        public Expression getRight() { return right; }
        @Override
        public boolean eval() {
            return pred.test(left, right);
        }
        @Override
        public String toString() {
            return left.toString() + " " + operator + " " + right.toString();
        }
    }
    public static class ParenExpression implements Expression {
       
        private final Expression expr;
       
        ParenExpression(Expression expr) {
            this.expr = expr;
        }
        public Expression getExpression() { return expr; }
       
        @Override
        public boolean eval() {
            return expr.eval();
        }
        @Override
        public String toString() {
            return "(" + expr.toString() + ")";
        }
    }
    public static class UnaryExpression implements Expression {
        private final Expression expr;
        private final char operator;
        private final Predicate<Expression> pred;
        UnaryExpression(Expression expr, char operator, Predicate<Expression> pred) {
            this.operator = operator;
            this.expr = expr;
            this.pred = pred;
        }
        public Expression getExpression() { return expr; }
        @Override
        public boolean eval() {
            return pred.test(expr);
        }
        @Override
        public String toString() {
            return operator + expr.toString();
        }
    }
    public static class Literal implements Expression {
        private final boolean value;
        public Literal(boolean value) { this.value = value; }
        @Override
        public boolean eval() {
            return value;
        }
        @Override
        public String toString() {
            return Boolean.toString(value);
        }
    }
    public static class Identifier implements Expression {
        private final String name;
        private boolean value;
        Identifier(String name) { this.name = name; }
        public String getName() { return name; }
        public void setValue(boolean value) { this.value = value; }
      
        public boolean eval() {
            return value;
        }
        @Override
        public String toString() {
            return name;
        }
    }
    private static final String FALSE = "false";
    private static final String TRUE = "true";
    public static final char OR = '\u2228';
    public static final char AND = '\u2227';
    public static final char NOT = '\u00ac';
    private static final char LEFT_PAREN = '(';
    private static final char RIGHT_PAREN = ')';
    private final String text;
    private int pos;
    private int ch;
    private Map<String, Identifier> identifiers = new HashMap<>();
    public LogicalTerm(String text) {
        this.text = text;
    }
    public Collection<Identifier> getIdentifiers() {
        return identifiers.values();
    }
    public Identifier getIdentifier(String name) {
        return identifiers.get(name);
    }
    public Expression compile() {
        pos = -1;
        advance();
        return parse();
    }
    private Expression parse() {
        Expression expression = parseTerm();
        while (true) {
            if (eat(OR)) {
                expression = or(expression, parseTerm());
            } else {
                return expression;
            }
        }
    }
    private Expression parseTerm() {
        Expression expression = parseOperand();
        while (true) {
            if (eat(AND)) {
                expression = and(expression, parseOperand());
            } else {
                return expression;
            }
        }
    }
    private Expression parseOperand() {
        if (eat(NOT)) return not(parseOperand());
        int startPos = pos;
        if (eat(LEFT_PAREN)) {
            Expression expression = parse();
            if (!eat(RIGHT_PAREN)) {
                throw new RuntimeException("Missing ')' at " + pos);
            }           
            return new ParenExpression(expression);
        } else if (Character.isLetter(ch)) {
            while (Character.isLetter(ch) || Character.isDigit(ch)) {
                advance();
            }
            String token = text.substring(startPos, pos);
            if (TRUE.equalsIgnoreCase(token)) return literal(true);
            else if (FALSE.equalsIgnoreCase(token)) return literal(false);
            identifiers.putIfAbsent(token, new Identifier(token));
            return identifiers.get(token);
        } else {
            throw new RuntimeException("Unexpected character " + ch + " at " + pos);
        }
    }
    private void advance() {
        int max = text.length();
        if (pos < max) {
            pos++;
        }
        if (pos < max) {
            ch = text.charAt(pos);
        } else {
            ch = -1;
        }
    }
    private boolean eat(char expected) {
        while (Character.isWhitespace(ch)) advance();
        if (ch == expected) {
            advance();
            return true;
        }
        return false;
    }
    private Expression or(Expression left, Expression right) {
        return new BinaryExpression(left, right, OR, (l, r) -> l.eval() || r.eval());
    }
    private Expression and(Expression left, Expression right) {
        return new BinaryExpression(left, right, AND, (l, r) -> l.eval() && r.eval());
    }
    private Expression not(Expression expr) {
        return new UnaryExpression(expr, NOT, (e) -> !e.eval());
    }
    private Expression literal(boolean value) {
        return new Literal(value);
    }
}

Java:
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Stream;
import java.awt.BorderLayout;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.text.BadLocationException;
public class Test {
    private DefaultTableModel model;
    public void createAndShowGui() {
        model = new DefaultTableModel();
        JTable table = new JTable(model);
        JTextField text = new JTextField(50);
        text.addActionListener(e -> table.setModel(createValueTable(text.getText())));
        JPanel inputText = new JPanel();
        inputText.add(new JLabel("Logischer Term: "));
        inputText.add(text);
        Box inputButtons = Box.createHorizontalBox();
        inputButtons.add(createInputButton(text, "" + LogicalTerm.NOT));
        inputButtons.add(createInputButton(text, "" + LogicalTerm.AND));
        inputButtons.add(createInputButton(text, "" + LogicalTerm.OR));
        Box input = Box.createVerticalBox();
        input.add(inputText);
        input.add(inputButtons);
       
        JScrollPane output = new JScrollPane(table);
        JPanel content = new JPanel(new BorderLayout());
        content.add(input, BorderLayout.NORTH);
        content.add(output);
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.add(content);
        frame.pack();
        frame.setVisible(true);
    }
    private DefaultTableModel createValueTable(String text) {
        LogicalTerm term = new LogicalTerm(text);
        LogicalTerm.Expression expr;
        try {
            expr = term.compile();
        } catch (RuntimeException ex) {
            return new DefaultTableModel(new Object[][] {{ex.getMessage()}},
                    new Object[]{"Fehlerhafter Ausdruck"});
        }
        List<LogicalTerm.Identifier> identifiers = term.getIdentifiers().stream()
            .sorted(Comparator.comparing(LogicalTerm.Identifier::getName))
            .toList();
        return createValueTable(identifiers, expr);
    }
    private DefaultTableModel createValueTable(List<LogicalTerm.Identifier> identifiers, LogicalTerm.Expression expr) {
        int varCount = identifiers.size();
        List<String> colNames = Stream.concat(
                identifiers.stream().map(LogicalTerm.Identifier::getName),
                Stream.of(expr.toString())
            ).toList();
        DefaultTableModel model = new DefaultTableModel(colNames.toArray(), 0);
        for (int bitset = 0, n = (1 << varCount); bitset < n; bitset++) {
            List<Boolean> values = new ArrayList<>();
            for (int i = 0; i < varCount; i++) {
                boolean value = (bitset & (1 << i)) > 0;
                identifiers.get(i).setValue(value);
                values.add(value);
            }
            values.add(expr.eval());
            model.addRow(values.toArray());
        }       
        return model;
    }
    private JButton createInputButton(JTextField text, String string) {
        JButton button = new JButton(string);
        button.addActionListener(e -> {
            try {
                text.getDocument().insertString(text.getCaretPosition(), string, null);
                text.requestFocus();
            } catch (BadLocationException ex) {
                ex.printStackTrace();
            }
        });
        return button;
    }
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new Test().createAndShowGui());
    }
}

1668818989235.png
 
Ok Wie stelle ich die Implikation dar? Äquivalenz wäre ja l.eval() == r.eval() oder? Und an welcher Stelle muss ich beide Operatoren einfügen?
 
Zuletzt bearbeitet:
Java:
 private Expression equivalence(Expression left, Expression right) {
        return new BinaryExpression(left, right, EQUIVALENCE, (l, r) -> l.eval()  == r.eval());
    }
Was mache ich falsch?
 
Java:
 private Expression equivalence(Expression left, Expression right) {
        return new BinaryExpression(left, right, EQUIVALENCE, (l, r) -> l.eval()  == r.eval());
    }
Was mache ich falsch?
Java:
 private Expression parse() {
        Expression expression = parseTerm();
        while (true) {
            if (eat(OR)) {
                expression = or(expression, parseTerm());
            } else if (eat(EQUIVALENCE)) {
                equivalence(expression, parseTerm());
            } else {
                return expression;
            }
        }
    }

oder muss ich das an einer anderen Stelle einfügen?
 
Du musst Dir die expression in Zeile 7 schon merken:
Java:
expression = equivalence(expression, parseTerm());
 
Danke wie kann ich true und false gegen 0 und 1 ersetzen und in aufsteigender Reihenfolge auflisten
Für die Eingabe müsstest Du in Zeile 160 (bezogen auf LogicalTerm.java in Kommentar #6) prüfen, ob ch == '0' || ch == '1' gilt. Ist das der Fall, erzeugst Du ein Literal mit dem Wert (0 oder 1). Die dazugehörige Klasse musst Du natürlich anpassen, dass im Konstruktor kein boolean mehr genommen wird und bei eval() halt false bei 0 ansonsten true zurückgegeben wird.

Und was heißt in aufsteigender Reihenfolge auflisten? Aktuell werden die 2^n Kombinationen (n = Anzahl der Variablen) der Reihe nach durchgegangen. Bei 0/1 für false/true, würde sich bei zwei Variablen halt 0,0; 0,1; 1,0; 1,1 ergeben.
 
Quick & Dirty:

Ansatz von https://stackoverflow.com/a/26227947/19657183 übernommen.

Java:
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Predicate;
import java.util.function.BiPredicate;

public class LogicalTerm {
    public static interface Expression {
        boolean eval();
    }
    public static class BinaryExpression implements Expression {
        private final Expression left;
        private final Expression right;
        private final char operator;
        private final BiPredicate<Expression, Expression> pred;
        BinaryExpression(Expression left, Expression right, char operator, BiPredicate<Expression, Expression> pred) {
            this.operator = operator;
            this.left = left;
            this.right = right;
            this.pred = pred;
        }
        public Expression getLeft() { return left; }
        public Expression getRight() { return right; }
        @Override
        public boolean eval() {
            return pred.test(left, right);
        }
        @Override
        public String toString() {
            return left.toString() + " " + operator + " " + right.toString();
        }
    }
    public static class ParenExpression implements Expression {
      
        private final Expression expr;
      
        ParenExpression(Expression expr) {
            this.expr = expr;
        }
        public Expression getExpression() { return expr; }
      
        @Override
        public boolean eval() {
            return expr.eval();
        }
        @Override
        public String toString() {
            return "(" + expr.toString() + ")";
        }
    }
    public static class UnaryExpression implements Expression {
        private final Expression expr;
        private final char operator;
        private final Predicate<Expression> pred;
        UnaryExpression(Expression expr, char operator, Predicate<Expression> pred) {
            this.operator = operator;
            this.expr = expr;
            this.pred = pred;
        }
        public Expression getExpression() { return expr; }
        @Override
        public boolean eval() {
            return pred.test(expr);
        }
        @Override
        public String toString() {
            return operator + expr.toString();
        }
    }
    public static class Literal implements Expression {
        private final boolean value;
        public Literal(boolean value) { this.value = value; }
        @Override
        public boolean eval() {
            return value;
        }
        @Override
        public String toString() {
            return Boolean.toString(value);
        }
    }
    public static class Identifier implements Expression {
        private final String name;
        private boolean value;
        Identifier(String name) { this.name = name; }
        public String getName() { return name; }
        public void setValue(boolean value) { this.value = value; }
     
        public boolean eval() {
            return value;
        }
        @Override
        public String toString() {
            return name;
        }
    }
    private static final String FALSE = "false";
    private static final String TRUE = "true";
    public static final char OR = '\u2228';
    public static final char AND = '\u2227';
    public static final char NOT = '\u00ac';
    private static final char LEFT_PAREN = '(';
    private static final char RIGHT_PAREN = ')';
    private final String text;
    private int pos;
    private int ch;
    private Map<String, Identifier> identifiers = new HashMap<>();
    public LogicalTerm(String text) {
        this.text = text;
    }
    public Collection<Identifier> getIdentifiers() {
        return identifiers.values();
    }
    public Identifier getIdentifier(String name) {
        return identifiers.get(name);
    }
    public Expression compile() {
        pos = -1;
        advance();
        return parse();
    }
    private Expression parse() {
        Expression expression = parseTerm();
        while (true) {
            if (eat(OR)) {
                expression = or(expression, parseTerm());
            } else {
                return expression;
            }
        }
    }
    private Expression parseTerm() {
        Expression expression = parseOperand();
        while (true) {
            if (eat(AND)) {
                expression = and(expression, parseOperand());
            } else {
                return expression;
            }
        }
    }
    private Expression parseOperand() {
        if (eat(NOT)) return not(parseOperand());
        int startPos = pos;
        if (eat(LEFT_PAREN)) {
            Expression expression = parse();
            if (!eat(RIGHT_PAREN)) {
                throw new RuntimeException("Missing ')' at " + pos);
            }          
            return new ParenExpression(expression);
        } else if (Character.isLetter(ch)) {
            while (Character.isLetter(ch) || Character.isDigit(ch)) {
                advance();
            }
            String token = text.substring(startPos, pos);
            if (TRUE.equalsIgnoreCase(token)) return literal(true);
            else if (FALSE.equalsIgnoreCase(token)) return literal(false);
            identifiers.putIfAbsent(token, new Identifier(token));
            return identifiers.get(token);
        } else {
            throw new RuntimeException("Unexpected character " + ch + " at " + pos);
        }
    }
    private void advance() {
        int max = text.length();
        if (pos < max) {
            pos++;
        }
        if (pos < max) {
            ch = text.charAt(pos);
        } else {
            ch = -1;
        }
    }
    private boolean eat(char expected) {
        while (Character.isWhitespace(ch)) advance();
        if (ch == expected) {
            advance();
            return true;
        }
        return false;
    }
    private Expression or(Expression left, Expression right) {
        return new BinaryExpression(left, right, OR, (l, r) -> l.eval() || r.eval());
    }
    private Expression and(Expression left, Expression right) {
        return new BinaryExpression(left, right, AND, (l, r) -> l.eval() && r.eval());
    }
    private Expression not(Expression expr) {
        return new UnaryExpression(expr, NOT, (e) -> !e.eval());
    }
    private Expression literal(boolean value) {
        return new Literal(value);
    }
}

Java:
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Stream;
import java.awt.BorderLayout;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.text.BadLocationException;
public class Test {
    private DefaultTableModel model;
    public void createAndShowGui() {
        model = new DefaultTableModel();
        JTable table = new JTable(model);
        JTextField text = new JTextField(50);
        text.addActionListener(e -> table.setModel(createValueTable(text.getText())));
        JPanel inputText = new JPanel();
        inputText.add(new JLabel("Logischer Term: "));
        inputText.add(text);
        Box inputButtons = Box.createHorizontalBox();
        inputButtons.add(createInputButton(text, "" + LogicalTerm.NOT));
        inputButtons.add(createInputButton(text, "" + LogicalTerm.AND));
        inputButtons.add(createInputButton(text, "" + LogicalTerm.OR));
        Box input = Box.createVerticalBox();
        input.add(inputText);
        input.add(inputButtons);
      
        JScrollPane output = new JScrollPane(table);
        JPanel content = new JPanel(new BorderLayout());
        content.add(input, BorderLayout.NORTH);
        content.add(output);
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.add(content);
        frame.pack();
        frame.setVisible(true);
    }
    private DefaultTableModel createValueTable(String text) {
        LogicalTerm term = new LogicalTerm(text);
        LogicalTerm.Expression expr;
        try {
            expr = term.compile();
        } catch (RuntimeException ex) {
            return new DefaultTableModel(new Object[][] {{ex.getMessage()}},
                    new Object[]{"Fehlerhafter Ausdruck"});
        }
        List<LogicalTerm.Identifier> identifiers = term.getIdentifiers().stream()
            .sorted(Comparator.comparing(LogicalTerm.Identifier::getName))
            .toList();
        return createValueTable(identifiers, expr);
    }
    private DefaultTableModel createValueTable(List<LogicalTerm.Identifier> identifiers, LogicalTerm.Expression expr) {
        int varCount = identifiers.size();
        List<String> colNames = Stream.concat(
                identifiers.stream().map(LogicalTerm.Identifier::getName),
                Stream.of(expr.toString())
            ).toList();
        DefaultTableModel model = new DefaultTableModel(colNames.toArray(), 0);
        for (int bitset = 0, n = (1 << varCount); bitset < n; bitset++) {
            List<Boolean> values = new ArrayList<>();
            for (int i = 0; i < varCount; i++) {
                boolean value = (bitset & (1 << i)) > 0;
                identifiers.get(i).setValue(value);
                values.add(value);
            }
            values.add(expr.eval());
            model.addRow(values.toArray());
        }      
        return model;
    }
    private JButton createInputButton(JTextField text, String string) {
        JButton button = new JButton(string);
        button.addActionListener(e -> {
            try {
                text.getDocument().insertString(text.getCaretPosition(), string, null);
                text.requestFocus();
            } catch (BadLocationException ex) {
                ex.printStackTrace();
            }
        });
        return button;
    }
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new Test().createAndShowGui());
    }
}

Anhang anzeigen 19666
Für die Eingabe müsstest Du in Zeile 160 (bezogen auf LogicalTerm.java in Kommentar #6) prüfen, ob ch == '0' || ch == '1' gilt. Ist das der Fall, erzeugst Du ein Literal mit dem Wert (0 oder 1). Die dazugehörige Klasse musst Du natürlich anpassen, dass im Konstruktor kein boolean mehr genommen wird und bei eval() halt false bei 0 ansonsten true zurückgegeben wird.

Und was heißt in aufsteigender Reihenfolge auflisten? Aktuell werden die 2^n Kombinationen (n = Anzahl der Variablen) der Reihe nach durchgegangen. Bei 0/1 für false/true, würde sich bei zwei Variablen halt 0,0; 0,1; 1,0; 1,1 ergeben.
Ok. Im obigen Beispiel ist die Ausgabe Reihenfolge aber nicht so
 
Ach, so meinst Du. Das kannst Du in createValueTable ändern:

Java:
identifiers.get(varCount - 1 - i).setValue(value);
 
Zuletzt bearbeitet:
Das Problem ist, dass die Werte der Reihe nach eingefügt werden, unabhängig von der Position. Am besten einfach noch eine Schleife drunter: erst Werte setzen, dann zur Tabelle hinzufügen.

Java:
        for (int bitset = 0, n = (1 << varCount); bitset < n; bitset++) {
            for (int i = 0; i < varCount; i++) {
                boolean value = (bitset & (1 << i)) > 0;
                identifiers.get(varCount - 1 - i).setValue(value);
            }
            List<Boolean> values = new ArrayList<>();
            for (LogicalTerm.Identifier id : identifiers) {
                values.add(id.eval());
            }
            values.add(expr.eval());
            System.out.println(java.util.Arrays.toString(values.toArray()));
            model.addRow(values.toArray());
            System.out.println(model.getDataVector());
        }
 
Ist es möglich über einen Button zu steuern, ob man Zwischenergebnisse sehen möchte? Z.B.bei a->NOT b die Spalten a b NOT b und a-> NOT B (mit Zwischenergebnis) oder nur a b und a-> NOT b (ohne Zwischenergebnis)
 
Hatte mir schon gedacht, dass etwas in der Richtung kommen wird, daher wurden im Code auch schon Vorbereitungen getroffen 🙂

Ich kann Dir aber nur den Ansatz sagen, denn das ist nicht mal eben gemacht: Der String wird in einen Abstract Syntax Tree geparst, der aus Expression-Objekten besteht. Die Wurzel dieses Baums erhältst Du per parse().

Zum Beispiel wäre NOT b AND a ein Baum der Art
Code:
    BinaryOperator(AND)
        /             \
       /               \
  UnaryOperator(NOT)  Identifier(A)
       |
   Identifier(B)

Du kannst nun den Typ der Expression prüfen und je nachdem hast Du Zugriff auf den bzw. die Teilausdrücke (UnaryOperator -> getExpression(), BinaryOperator -> getLeftExpression() und getRightExpression()). Eine String-Darstellung einer jeden Expression erhältst Du per toString() und das Ergebnis per eval().

Du könntest also hergehen, durch den Baum laufen und BinaryExpressions auslesen.
 

Zurück
Oben