右から左への評価プログラムを左から右への評価プログラムに変換する必要がある割り当てがあります (Y/N プロンプトで複数の式を評価するように求められる場合もそうします)。プログラムにいくつかの変更を加える必要がありました。
- 数字と演算子の間にスペースを許可する
- 負の数には、マイナス記号の直後に数字または小数点が続く必要があります。さらに、負の数は通常、括弧で囲む必要があります。(これは、ネガティブにアンダースコアを使用する古いプログラムとは異なります)
- 最高の優先順位で累乗を許可する必要があります。
- 評価は左から右に行う必要があります。これが主な変更点です。A) formNum() メソッドは、数値の左端の桁または先頭の小数点に遭遇したときに左から右にスキャンするように書き直す必要があります。もちろん、左から右にスキャンしているため、連続する数字から double 値を構築する方法を調整する必要があります。いくつかの例を試して、正しい方法を見つけてください。B) evaluator() メソッドも、左から右にスキャンするように調整する必要があります。いくつかの変更があります: i) 左括弧が検出されると、負の数 (5+ (-13.4) +2 など) を通知しない限り、常にスタック A にプッシュされます。その場合、formNum() を呼び出して負の数を計算し、それを double として 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 を計算する必要があります。
これを念頭に置いて、ここに古いプログラム ファイルを示します。//古いプログラム ファイルをペーストビン リンクに入れて、読みやすくしました。
ExpressionEvaluator.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() の先頭を指します。何が悪いのかわかりません。誰かが私を助けることができれば、それは大歓迎です。