我有一个任务,我需要将一个从右到左的求值程序转换为一个从左到右的求值程序(也让它要求你用 Y/N 提示来评估多个表达式)。我需要对程序进行一些更改,例如
- 允许数字和运算符之间有空格
- 负数应该有一个减号,后面紧跟一个数字或小数点;此外,负数通常应该用括号括起来。(这与使用下划线表示否定的旧程序不同)
- 应该允许取幂,具有最高优先级。
- 评价应该从左到右。这是主要变化。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() 的开头。我不知道出了什么问题。如果有人可以帮助我,将不胜感激。