【问题标题】:How to dynamically add brackets to a boolean expression?如何动态地将括号添加到布尔表达式?
【发布时间】:2015-06-08 20:54:43
【问题描述】:

我正在用 Java 编写一个程序,该程序应该用作布尔表达式的真值表生成器。但是,我在使用括号时遇到了很多麻烦。当一对括号括住整个表达式(产生运行时错误)或表达式中没有足够的括号(产生错误结果)时,错误开始。

这是我所有的代码:

        import javax.swing.*;
        import java.awt.*;
        import java.awt.event.*;
        import java.util.*;
        import java.lang.Math;

        public class BooleanAlgebra
        {
           public static void main(String[] args)
           {
              new BooleanAlgebra();
           }

           private JFrame frame;
           private JPanel panel;
           private JTextField textField;
           private JButton and;
           private JButton or;
           private JButton not;
           private JButton nor;
           private JButton nand;
           private JButton xor;
           private JButton xnor;
           private JPanel panel2;
           private JButton generate;
           private JButton close;
           private ArrayList<Character> variables;
           private char[] chars;

           public BooleanAlgebra()
           {
              frame = new JFrame("Boolean Algebra");
              frame.setSize(new Dimension(800, 800));
              textField = new JTextField("(x+y)");

              and = new JButton("AND   [ x & y ]");
              or = new JButton("OR   [ x + y ]");
              not = new JButton("NOT   [ ! x ]");
              nor = new JButton("NOR   [ ! ( x + y ) ]");
              nand = new JButton("NAND   [ ! ( x & y ) ]");
              xor = new JButton("XOR   [ ( x & ! y ) + ( ! x & y ) ]");
              xnor = new JButton("XNOR   [ ( x & y ) + ( ! x & ! y ) ]");
              generate = new JButton("Generate Table");
              close = new JButton("Exit");

              ButtonHandler buttons = new ButtonHandler();
              and.addActionListener(buttons);
              or.addActionListener(buttons);
              not.addActionListener(buttons);
              nor.addActionListener(buttons);
              nand.addActionListener(buttons);
              xor.addActionListener(buttons);
              xnor.addActionListener(buttons);
              generate.addActionListener(buttons);
              close.addActionListener(buttons);

              panel2 = new JPanel();
              panel2.setLayout(new GridLayout(1, 2));
              panel2.add(generate);
              panel2.add(close);

              panel = new JPanel();
              panel.setLayout(new GridLayout(9, 1));
              panel.add(textField);
              panel.add(and);
              panel.add(or);
              panel.add(not);
              panel.add(nor);
              panel.add(nand);
              panel.add(xor);
              panel.add(xnor);
              panel.add(panel2);

              frame.add(panel);
              frame.setLocationRelativeTo(null);
              frame.setVisible(true);
              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
           }

           private boolean isLetter(char a)
           {
              if((a > 64 && a < 91) || (a > 96 && a < 123))
              {
                 return true;
              }
              return false;
           }

           private void generate()
           {
              chars = ("(" + textField.getText() + ")").toCharArray();
              variables = new ArrayList<Character>();

              for(int i = 0; i < chars.length; i++)
              {
                 if(isLetter(chars[i]))
                 {
                    if(!variables.contains(chars[i]))
                    {
                       variables.add(chars[i]);
                    }
                 }
              }

              Collections.sort(variables);
              String[] heads = new String[variables.size() + 1];
              for(int i = 0; i < heads.length - 1; i++)
              {
                 heads[i] = variables.get(i).toString();
              }
              heads[heads.length - 1] = textField.getText();

              int row = (int)Math.pow(2, variables.size());
              int column = variables.size() + 1;
              int count = 0;
              int max = 1;
              Integer[][] array = new Integer[row][column];

              for(int a = column - 2; a >= 0; a--)
              {
                 for(int b = 0; b < row; b++)
                 {
                    if(count < max)
                    {
                       array[b][a] = 0;
                       count++;
                    }
                    else if(count >= max)
                    {
                       array[b][a] = 1;
                       count++;
                       if(count == max * 2)
                       {
                          count = 0;
                       }
                    }
                 }
                 max = max * 2;
              }

              for(int i = 0; i < row; i++)
              {
                 array[i][column - 1] = 0;
              }

              int[][] arrayCopy = new int[row][column];
              for(int i = 0; i < row; i++)
              {
                 for(int j = 0; j < column; j++)
                 {
                    arrayCopy[i][j] = array[i][j];
                 }
              }
              for(int i = 0; i < row; i++)
              {
                 array[i][column - 1] = calculate(arrayCopy[i]);
              }

              JTable table = new JTable(array, heads);
              JFrame frame2 = new JFrame(textField.getText());
              frame2.add(new JScrollPane(table));
              frame2.pack();
              frame2.setVisible(true);
              table.setEnabled(false);
           }

           private int calculate(int[] array)
           {
              Stack<Character> ops  = new Stack<Character>();
              Stack<Integer> values = new Stack<Integer>();

              for(int i = 0; i < chars.length; i++)
              {
                 char s = chars[i];
                 if      (s == '(')               ;
                 else if (s == '+')    ops.push(s);
                 else if (s == '&')    ops.push(s);
                 else if (s == '!')    ops.push(s);
                 else if (s == ')')
                 {
                    char operation = ops.pop();
                    int v = values.pop();
                    if      (operation == '+')    v = or(values.pop(), v);
                    else if (operation == '&')    v = and(values.pop(), v);
                    else if (operation == '!')    v = not(v);
                    values.push(v);
                 }
                 else if(isLetter(s))
                 {
                    values.push(array[variables.indexOf(s)]);
                 }
              }
              return values.pop();
           }

           private int and(int a, int b)
           {
              if(a == 1 && b == 1)
              {
                 return 1;
              }
              return 0;
           }

           private int or(int a, int b)
           {
              if(a == 0 && b == 0)
              {
                 return 0;
              }
              return 1;
           }
           private int not(int a)
           {
              if(a == 1)
              {
                 return 0;
              }
              return 1;
           }

           public class ButtonHandler implements ActionListener
           {
              public void actionPerformed(ActionEvent event)
              {
                 if(event.getSource() == and)
                 {
                    textField.setText(textField.getText() + "(x&y)");
                    textField.requestFocus(true);
                 }
                 if(event.getSource() == or)
                 {
                    textField.setText(textField.getText() + "(x+y)");
                    textField.requestFocus(true);
                 }
                 if(event.getSource() == not)
                 {
                    textField.setText(textField.getText() + "(!x)");
                    textField.requestFocus(true);
                 }
                 if(event.getSource() == nor)
                 {
                    textField.setText(textField.getText() + "!(x+y)");
                    textField.requestFocus(true);
                 }
                 if(event.getSource() == nand)
                 {
                    textField.setText(textField.getText() + "!(x&y)");
                    textField.requestFocus(true);
                 }
                 if(event.getSource() == xor)
                 {
                    textField.setText(textField.getText() + "(x&(!y))+(((!x)&y))");
                    textField.requestFocus(true);
                 }
                 if(event.getSource() == xnor)
                 {
                    textField.setText(textField.getText() + "(x&y)+(((!x)&(!y)))");
                    textField.requestFocus(true);
                 }
                 if(event.getSource() == generate)
                 {
                    generate();
                 }
                 if(event.getSource() == close)
                 {
                    System.exit(0);
                 }
              }
           }
        }

我需要帮助来完成这项工作,而无需用户自己正确输入括号(我希望程序修复表达式,或者以某种方式评估它而无需像我在这里那样等待右括号)。

要测试代码并查看错误,请在运行时输入以下内容:

 (x+y) --> runtime error because brackets surround the whole expression
 x+y+z --> incorrect output table
 x+y   --> works
 x&y   --> works
 !!x   --> incorrect output table

请给我解释一下。谢谢!

【问题讨论】:

  • 具体说明运行时错误和错误结果。
  • 你需要编写某种 LL(1) 解析器来正确解析它。
  • 错误的结果意味着应该有 1 的地方有 0,反之亦然。运行时错误涉及 EmptyStackException。

标签: java


【解决方案1】:

在您的 generate 方法中,您声明了以下内容:

chars = ("(" + textField.getText() + ")").toCharArray();

但是,在您的 GUI 中,实际上有一个带有大括号的条目,例如 (x+y),它基本上将 chars 的值更改为 ((x+y))

calculate 中,您在关闭) 字符时弹出第二个大括号返回EmptyStackException。如果您从一般文本字段或从表格上方引用的行中删除大括号,则应该可以打印出来。

关于(!!x) 的输入失败:您只弹出堆栈中的一个操作,而忽略是否有多个操作可用。


当您询问如何解决仅使用一个运算符的问题时:

您可以尝试将a &amp;&amp; b || (c &amp;&amp; d) &amp;&amp; !e 之类的等式分解为自己的部分,然后评估每个术语,最后将所有这些术语组合成类似于an earlier post I did 的结果,它在纯Java 中提供了一个简单的规则引擎。但是请注意,我没有保证绑定强度的正确性,也没有在这个示例中包含大括号 - 因此它只是一个非常基本的规则引擎演示!

遵循 OO 原则,您可以创建一个 Expression 类,其中 AndOrNotTerm... 是其子级,并包含一个 boolean evaluate() 方法,该方法将调用向下传播各自的条款及其约束力。 因此,上面的方程可以用类似于这个 f.e 的更像代码的方法来表示:

Expression terms = new Or(
    new And(new Term("a", ...), new Term("b", ...)), 
    new And(new Term("c", ...), 
        new And(new Term("d", ...), new Not(new Term("e", ...))
    )
);
boolean value = terms.evaluate();

要获得与上面类似的结构,您基本上从一个空堆栈开始,并遍历方程中的所有标记。堆栈将保存当前打开的表达式。因此,您首先检索第一个字符并确定它是操作数((!)还是字母。如果找到一个表达式,您可以为其创建一个新的类表示并将其添加到堆栈中,否则基于该字母创建一个术语对象并将相应的 array[variables.indexOf(s)] 值分配给 0 或 1(false 或 true)学期。

然后检查堆栈上是否有元素(peek 将返回堆栈中最顶部的元素而不删除它),如果有,则将术语添加到操作数,否则将其临时存储,以便您可以添加它直到您点击操作数并在其后添加。如果操作数的所有字段都已填满,则需要将其从堆栈中弹出并将其分配(如果尚未完成)到堆栈上的新最顶层元素。这样就可以实现嵌套结构。

但是,您还必须考虑操作的绑定顺序。大括号的绑定比 and 操作更强,而 and 操作比 or 操作更牢固。因此,在执行新操作时,如果绑定顺序强于当前元素的绑定顺序,您还需要检查堆栈中最顶层的元素。 F.e.在从堆栈上方迭代样本时,当前持有abAnd 操作,我们目前正在处理Or 操作。由于 this 的绑定比 And 弱,我们需要从堆栈中弹出这个元素并将其分配给 Or 操作的左侧元素并将其压入堆栈。

这与大括号类似。但是,它们可以包含多个表达式,并且只有在解析了右大括号时才需要从堆栈中弹出。

我希望这可以为您提供足够的见解来继续您的旅程。

【讨论】:

  • 是的,我意识到这一点,但我仍然无法输入 x+y+z 来显示正确的结果。我也不知道如何处理多个操作,如果可能的话,我需要一个例子。
  • 另外,我无法预测用户是否会输入 x+y 或 (x+y) 之类的内容,这就是为什么我有括号以帮助用户。但我的问题是我希望能够同时处理 (x+y) 的条目和 x+y 的条目,并为两者产生相同的结果。这就是为什么我需要能够在代码本身中动态地将括号添加到 x+y 以将其更改为 (x+y)。
  • @user3314809 更新了代码,提供了有关如何处理多个操作和术语同时仍然能够评估它们的更多详细信息
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-07-09
  • 1970-01-01
  • 1970-01-01
  • 2011-01-08
  • 1970-01-01
  • 1970-01-01
  • 2012-09-15
相关资源
最近更新 更多