0

我有一个任务,我需要将一个从右到左的求值程序转换为一个从左到右的求值程序(也让它要求你用 Y/N 提示来评估多个表达式)。我需要对程序进行一些更改,例如

  1. 允许数字和运算符之间有空格
  2. 负数应该有一个减号,后面紧跟一个数字或小数点;此外,负数通常应该用括号括起来。(这与使用下划线表示否定的旧程序不同)
  3. 应该允许取幂,具有最高优先级。
  4. 评价应该从左到右。这是主要变化。A) 必须重写 formNum() 方法,以便在遇到数字的最左边数字或前导小数点时从左到右扫描。当然,由于您是从左到右扫描,因此必须调整从连续数字构建双精度值的方法 - 尝试一些示例以发现正确的方法。B) evaluator() 方法也必须调整为从左到右扫描。有几个变化: i) 当遇到左括号时,它总是被压入堆栈 A,除非它发出负数信号(如 5+ (-13.4) +2)。在这种情况下,应该调用 formNum() 来计算负数并将其作为双精度数推送到 B 上,并且应该跳过相应的右括号。ii) 当遇到右括号(但不是负数的右端)时,它会触发对 evalDown() 的调用,以评估带括号的子表达式中的操作,直到到达相应的左括号。左括号也从 A 中弹出。 iii) 当当前令牌运算符的优先级等于堆栈顶部的运算符时,不要压入令牌。相反,使用 eval() 在 A 堆栈顶部执行操作,然后将当前令牌运算符与 A 堆栈的新顶部进行比较。例子。假设我们有 3.0 / 2.0 * 4.0 。我们将 3 推到 B 上,将 / 推到 A 上,然后将 2 推到 B 上。如果我们(错误地)将 * 推到 A 上(它与 / 具有相同的优先级),然后将 4 推到 B 上,我们将先计算 2 * 4,然后再计算 3/ 8.. 相反,我们应该先计算 3./2 并推入 1.5,然后再计算 1.5 * 4 = 6.0。

考虑到这一点,这里是旧程序文件//我已将旧程序文件放在 pastebin 链接中,以使可读性更容易一些。

表达式评估器.java https://pastebin.com/QkKdU6dh

ExprEvaluatorTest.java https://pastebin.com/3UqqLsvr

Stack1gen.java https://pastebin.com/KW4Pz9Pt

这是我对新程序的尝试。

LR.java

import java.util.Scanner;

//class for evaluating arithmetic expressions as double values, which are displayed rounded to 7 places,
//with decimal points supressed for whole numbers. The unrounded double value is returned.
public class LR
{
  Scanner kb =  new Scanner(System.in); //allaws spaces

  //class-wide variables:
  //Since expressions are evaluated one at a time, all evaluations can use the same two stacks.
  private static Stack1gen<Character> A = new Stack1gen<Character>(); //stack for operators
  private static Stack1gen<Double> B = new Stack1gen<Double>(); //stack for operands

  //class-wide methods:

  //method to print a double value that is an integer as an int
  public static void expressionOutput(double x)
  {
    if(x == (double)Math.round(x)) //if the answer is a whole number,
                                   //display without decimal point
    {
       int intAns = (int)x;
       System.out.println("value = " + intAns + '\n');
    }
    else
    {
       System.out.println("value = " + x + '\n');
    }
  }


  /*Expressions are evaluated from right to left, using a stack A of arithmetic
  operators and a stack B of operands. In this method, a single operation is
  evaluated by popping the current operator from A, its 2 operands from B, and by then
  performing the operation and pushing the result onto B as a double value.*/
  
  private static void eval()
  {
      char op = A.pop(); //current operator
      double opnd1 = B.pop(); //operands
      double opnd2 = B.pop();
      double val = 0.0;
      switch (op) //evaluate
      {
        case '+':
          val = opnd1 + opnd2;
          break;
        case '-':
          val = opnd1 - opnd2;
          break;
        case '*':
          val = opnd1 * opnd2;
          break;
        case '/':
          val = opnd1/opnd2;
          break;
        case '^':
          val = Math.pow(opnd1, opnd2); //update: allow for exponation
          break;
      }
      B.push(val); //push result onto B
  }

  /* In this method, a parenthesized subexpression is
  evaluated by evaluating its operations on Stack A top-down
  until a right parenthesis is encountered on stack A;
  the right parenthesis is also popped from A*/
  
  private static void evalDown()
  {
    do
    {
      eval();
    }while((A.getSize()>0) && (A.getTop() != ')'));
    if((A.getSize()>0) && (A.getTop() == ')'))
    {
      A.pop();
    }
  }
  
 
  //This method compares the current operator token in the input string to the operator
  //on top of stack A to determine precedence.
  private static boolean prec(char token, Stack1gen<Character> StackA)
  {
    char topOp = StackA.getTop();

    if(token == '^'){// update:exponents take max prec

      return true;
    }


    else if((token == '*') || (token == '/'))
    {
      return true; //token has precedence, so it will be pushed onto A
    }
    else
    {
      if((topOp == '*') || (topOp == '/'))
      {
        return false; //operator at top of A has precedence
      }
      else
      {
        return true; //equal low-precedence operators or token is ')',
                     // which is always pushed onto A
      }
    }
  }

  //variables for an ExprEvaluator object
  private String e;
  private int p; //pointer to characters within the expression string e
 
  //constructor 
  public LR()
  {
    System.out.println("enter an expression");
    e = kb.nextLine(); //input an arithmetic expression as a line of keyboard input.
    e = e.replaceAll(" ", ""); // Update: gets rid of all spaces from input, allowing spaces in input.
    p = e.length()-1;      
  }

  //parameterized constructor
  public LR(String ee)
  {
    e = ee;
    p = ee.length() - 1;  
  }

   public String getExpression()
       {
          return e;
       }


  //If a substring of e whose rightmost character is at position p 
  //represents a number (possibly with a decimal point, possibly negative), 
  //return the numerical value of the substring as a double value and
  //re-position p just to the left of the substring.
 
  private double formNum() // im not sure if i swaped the order corectly
  {
    double total = 0.0;
    int count = 0;
    int flag = 0;
    double mult = 1.0;
    char c,d;
    do
    {
      c = e.charAt(p); //examine the current character in the string (from right to left)
      if(c == '.')
      {
        flag = 1; //set a flag to remember that the character is a decimal point
      }
      else
      {
      //if the current character is a digit, convert to its
      //int value and include it in the value corresponding to the string.
        if((c >= '0') && (c<= '9')) 
        {
          total = total + mult * (c-'0');
          mult = mult * 10.0;
          if(flag == 0)
          {
           count++; //count the number of digits to the right of a possible decimal point
          }
        }
        else
        {
          if(c == '-' ) 
          {
            total = -total; 
          }
        }
      }
      p++; //Prepare to move to the next character to the right. 
           //This is a private non-static method because it changes the member variable p
      d = '?';
      if(p<= 0)
      {
         d = e.charAt(p); //the next character to the right
      }
    }while((p>=0) && (((d<='9')&&(d>='0'))||(d=='-' && d-1 =='(')||(d=='.')));//check for a valid character //upate: uses - with a check is the previous char is ( // im not sure this is right
    if(flag==1)
    {
     total = total/Math.pow(10.0,count*1.0); //compute the value taking into account
                                             //the number of decimal places
    }
    return total;
  }

  //This method uses the 2-stack approach to evaluate an expression (from right to left).
  //The result is rounded to 7 decimal places and displayed as explained in expressionOutput() above.
  //The original double value (unrounded) is returned to the calling program.
  //The code could be made more efficient (but more difficult to read) by using if-else-if-else...
  //as in formNum(); here we (unnecessarily) execute each if statement.
  public double evaluator() 
  {
 
    char token; //current token in the input expression
 
    //loop to scan the string right to left
    do
    {    
      // if right perentheses, evaluate and pop of that A stack
      token = e.charAt(p);
      if(token == ')')
      {
        if(token-1!='-'){
          evalDown();
          A.pop();
        }

        p++;      
      }
      
      //if the token is a left parenthesis, 
      //A push
      //else B push reults from form num
      if(token == '(' )
      {
        if(token+1!='-'){
        A.push(token);
        }
        else{
          B.push(formNum());
        }
        
        p++; //move
      }

      //method that checks for higher prec
      if((token=='+')||(token=='-')||(token=='*')||(token=='/')||(token=='^'))
      {          
        if((A.getSize() == 0) || (prec(token, A) == true))
        {
          eval();
          p++;         
        }
        //If the token is an arithmetic operator of lower precedence than that
        //at the top of the stack, then evaluate the operation at the top of stack A.
        else
        {
          eval();
        }
      }
      
      //if the token is the rightmost  digit of a number or a decimal point on the right,
      //form the number as a double and push onto stack B
      if(((token<='9')&&(token>='0'))||(token=='.'))
      {   
         B.push(formNum());
      }
   
    }while(p >= 0);//continue to scan from right to left


    //after completely scanning the input string, evaluate any remaining operations
    while(A.getSize()>0) 
    {
      eval();
    }
    
    double x = B.pop();

    //round the result to 7 places and then display
    expressionOutput((double)Math.round(x*10000000)/10000000.0); 

    return x; //return the original double value
  } //end of evaluator


} //end of class

LRTest.java

import java.util.Scanner;

/**
 * This is a tester class for expresions
 * 
 * I can not get this code to work so i am submitting what i have
 */
public class LRTest {

   public static void main(String[] args) {
      char choice;

      try (Scanner kb = new Scanner(System.in)) {
         System.out.println("would you like to enter a choice? Y/N");
         choice = kb.next().charAt(0);
         while ((choice == 'y') || (choice == 'Y')) {
            System.out.println("Enter an expression.");
            LR expr = new LR(kb.nextLine());
            System.out.println(expr.getExpression() + '\n');
            expr.evaluator(); // evaluate expr1
            System.out.println("would you like to enter another experesion? Y/N");
            choice = kb.next().charAt(0);

         }
         kb.close();
         // do
         // {
         // // Get the choice from the user to add more number
         // System.out.print(" Would you like to doo another expresion. Enter Y for yes
         // or N for no: ");
         // choice = kb.next().charAt(0);
         // System.out.println("enter an expresion");
         // LR expr = new LR( kb.nextLine());
         // System.out.println(expr.getExpression() + '\n');
         // expr.evaluator(); //evaluate expr1
         // }
         // while ((choice == 'y') || (choice == 'Y'));
         // kb.close();
      }
   }

}

和 Stack1gen.java // 编辑:不小心为这个类输入了错误的代码

//Stack1gen.java
//array implementation of stack class
 
public class Stack1gen<T>
{
  int MAX = 30; //maximum number of stack entries
 
  private int top;
  private T[] stack; //array to hold the stack data
 
  //default constructor
  public Stack1gen()
  {
    top = MAX; //initialize an empty stack
    stack = (T[]) new Object[MAX];
  }
 
  //copy constructor
//  public Stack1(Stack1 s)
//  {
//    top = s.top;
//    for(int i = top; i<=MAX-1; i++)
//   {
//      stack[i] = s.stack[i];
//    }
//  }
 
  public void push(T y)  //push data item y onto the stack
  {
    assert(top > 0); //check that there is room on the stack
    top = top -1;
    stack[top] = y;
  }
 
  public T pop() //pop the top item from the stack and return it
  {
    assert(top < MAX); //check that there is data on the stack
    T x = stack[top];
    top = top +1;
    return x;
  }
 
  public int getSize()
  {
     return MAX-top;
  }
 
  public T getTop()
  {
    assert(top < MAX);
    return stack[top];
  }
 
// public void printStack()  //print the contents of the stack, from
// top to bottom, one item per line, without popping the stack items.
 
 
}

当我运行 LRtest.java 并输入 y 时,我得到一个 StringIndexOutOfBoundsException ,它指向 evaluator() 的开头。我不知道出了什么问题。如果有人可以帮助我,将不胜感激。

4

0 回答 0