求解表达式的简单 Java 计算器
Simple Java Calculator to Solve Expressions
我正在研究求解表达式的计算器。我想弄清楚如何让它按 PEMDAS 顺序计算。我有一个 for 循环来遍历数组列表和一个调用 class 进行数学运算的开关。我已经尝试过 if 语句,但一直无法弄清楚。
我如何更改它以确保表达式以正确的顺序求解?这是我目前所拥有的:
/*
Peter Harmazinski
Simple Calculator
This program solves expressions
*/
import java.util.*;
public class SimpleCalculator2 {
static SimpleMath math = new SimpleMath();
public static void main(String[] args) {
Scanner console = new Scanner(System.in);
boolean again = true;
double number1 = 0.0;
double number2 = 0.0;
double answer = 0.0;
double results = 0.0;
String delims = "[ ]+";
getIntroduction();
while (again) {
System.out.println("Please enter your expression.");
String input = console.nextLine();
System.out.println("This is the user's input: " + input);
//Parses string into array list
List<String> list = new ArrayList<String>(Arrays.asList(input.split(delims)));
System.out.println("list: " + list);
results = doMath(list, number1, number2);
getResults(results);
}
console.close();
}
public static void getIntroduction() {
System.out.println("This is a simple calculator that solves expressions.");
}
//Traverses array list to identify operators and does math for surrounding numbers
//then answer is inserted in i-1 element and the elements i and i+1 are deleted
public static double doMath(List<String> list, double number1, double number2) {
double answer = 0.0;
double results = 0.0;
while (list.size() > 1) {
for (int i = 0; i < list.size(); i++) {
switch (list.get(i)) {
case "*" :
number1 = Double.parseDouble(list.get(i - 1));
number2 = Double.parseDouble(list.get(i + 1));
answer = math.multiply(number1, number2);
System.out.println(answer);
list.add(i - 1, Double.toString(answer));
list.subList(i, i + 3).clear();
System.out.println(list);
break;
case "/" :
number1 = Double.parseDouble(list.get(i - 1));
number2 = Double.parseDouble(list.get(i + 1));
answer = math.divide(number1, number2);
System.out.println(answer);
list.add(i - 1, Double.toString(answer));
list.subList(i, i + 3).clear();
System.out.println(list);
break;
case "+" :
number1 = Double.parseDouble(list.get(i - 1));
number2 = Double.parseDouble(list.get(i + 1));
answer = math.add(number1, number2);
System.out.println(answer);
list.add(i - 1, Double.toString(answer));
list.subList(i, i + 3).clear();
System.out.println(list);
break;
case "-" :
number1 = Double.parseDouble(list.get(i - 1));
number2 = Double.parseDouble(list.get(i + 1));
answer = math.subtract(number1, number2);
System.out.println(answer);
list.add(i - 1, Double.toString(answer));
list.subList(i, i + 3).clear();
System.out.println(list);
break;
}
}
}
return answer;
}
public static void getResults(double results) {
System.out.println("Results are: " + results);
}
}
我认为实现此目的的一个非常标准的算法是 Dijkstra 的调车场算法,然后是后缀评估。你可以在这里阅读它,伪代码在这里,但你可能需要对基本数据结构有一些了解: http://en.wikipedia.org/wiki/Shunting-yard_algorithm 。
如果您不了解堆栈、队列和后缀表示法,您可以编写一个更慢、更直接但更混乱的实现。我也用 sin、cos、trig、log 尝试过一次,结果代码运行正常,但我不会再试一次。
基本上,我们的想法是只用 1 个运算符找到最高优先级的表达式,对其求值,然后用它替换。这是一些伪代码:
input = [user input]
while expression still contains (, ), +, -, *, or /:
toEvaluate = highest priority expression with 1 operator (e.g. 1+2, or 2*3)
calculate the decimal value of toEvaluate
modify input so that you replace toEvaluate with its decimal value
请注意,在您的 doMath()
实现中,在您的 for 循环中,您只需在看到所有运算符时立即对其进行评估。例如,考虑
1+2*3
您将首先看到 +,计算 1+2,然后将结果乘以 3。相反,您需要先遍历整个列表,找到优先级最高的运算符,对其求值,然后从头开始再次.
既然你要将它输入到一个字符串中,为什么不制作一个遍历每个字符的 for 循环?
// This for loop will get you the string with only one operator in it.
// EX: 30+32
// You will have to find some way to compute this string.
for(int i = 0; i < userInput.length(); i++)
{
//int a will be the INDEX of the first operator
//int b will be the INDEX of the place where a new operator comes up
char c = userInput.getCharAt(i);
a = 0;
//I'm just gonna do it with + and - for now. Add any other operators.
if(c == '+' || c == '-')
{
b = i;
String stringToCompute = userInput.substring(a,b);
//Find some way to take the string there then compute it.
//Maybe another for loop with stringToCompute to find where the
// operator is and then add/subtract the two doubles.
// Now reset it
a = i;
b = null;
}
}
这里有几个可能的设计来解决这个问题:
您可以将处理分为两部分:解析和求值。在解析阶段,您将字符串转换为表示将如何对其进行评估的数据结构。在评估阶段,您遍历树并对表达式进行评估。
您可以按运算符拆分列表以评估最后(加号和减号),评估每个段(乘法和除法)。如果设计得当,这可能是递归的。
第一个选项更好,因为它更可扩展,但实施起来也更费力。这是通过解析和评估生成的示例数据结构:
interface Term {
double getValue();
}
enum Operator {
MULTIPLY, DIVIDE, ADD, SUBTRACT;
double getValue(List<Double> operands) {
...
}
}
class Operation implements Term {
List<Term> operands;
Operator operator;
double getValue() {
return operator.getValue(operands);
}
}
class Constant implements Term {
private final double value;
double getValue() {
return value;
}
}
我正在研究求解表达式的计算器。我想弄清楚如何让它按 PEMDAS 顺序计算。我有一个 for 循环来遍历数组列表和一个调用 class 进行数学运算的开关。我已经尝试过 if 语句,但一直无法弄清楚。
我如何更改它以确保表达式以正确的顺序求解?这是我目前所拥有的:
/*
Peter Harmazinski
Simple Calculator
This program solves expressions
*/
import java.util.*;
public class SimpleCalculator2 {
static SimpleMath math = new SimpleMath();
public static void main(String[] args) {
Scanner console = new Scanner(System.in);
boolean again = true;
double number1 = 0.0;
double number2 = 0.0;
double answer = 0.0;
double results = 0.0;
String delims = "[ ]+";
getIntroduction();
while (again) {
System.out.println("Please enter your expression.");
String input = console.nextLine();
System.out.println("This is the user's input: " + input);
//Parses string into array list
List<String> list = new ArrayList<String>(Arrays.asList(input.split(delims)));
System.out.println("list: " + list);
results = doMath(list, number1, number2);
getResults(results);
}
console.close();
}
public static void getIntroduction() {
System.out.println("This is a simple calculator that solves expressions.");
}
//Traverses array list to identify operators and does math for surrounding numbers
//then answer is inserted in i-1 element and the elements i and i+1 are deleted
public static double doMath(List<String> list, double number1, double number2) {
double answer = 0.0;
double results = 0.0;
while (list.size() > 1) {
for (int i = 0; i < list.size(); i++) {
switch (list.get(i)) {
case "*" :
number1 = Double.parseDouble(list.get(i - 1));
number2 = Double.parseDouble(list.get(i + 1));
answer = math.multiply(number1, number2);
System.out.println(answer);
list.add(i - 1, Double.toString(answer));
list.subList(i, i + 3).clear();
System.out.println(list);
break;
case "/" :
number1 = Double.parseDouble(list.get(i - 1));
number2 = Double.parseDouble(list.get(i + 1));
answer = math.divide(number1, number2);
System.out.println(answer);
list.add(i - 1, Double.toString(answer));
list.subList(i, i + 3).clear();
System.out.println(list);
break;
case "+" :
number1 = Double.parseDouble(list.get(i - 1));
number2 = Double.parseDouble(list.get(i + 1));
answer = math.add(number1, number2);
System.out.println(answer);
list.add(i - 1, Double.toString(answer));
list.subList(i, i + 3).clear();
System.out.println(list);
break;
case "-" :
number1 = Double.parseDouble(list.get(i - 1));
number2 = Double.parseDouble(list.get(i + 1));
answer = math.subtract(number1, number2);
System.out.println(answer);
list.add(i - 1, Double.toString(answer));
list.subList(i, i + 3).clear();
System.out.println(list);
break;
}
}
}
return answer;
}
public static void getResults(double results) {
System.out.println("Results are: " + results);
}
}
我认为实现此目的的一个非常标准的算法是 Dijkstra 的调车场算法,然后是后缀评估。你可以在这里阅读它,伪代码在这里,但你可能需要对基本数据结构有一些了解: http://en.wikipedia.org/wiki/Shunting-yard_algorithm 。
如果您不了解堆栈、队列和后缀表示法,您可以编写一个更慢、更直接但更混乱的实现。我也用 sin、cos、trig、log 尝试过一次,结果代码运行正常,但我不会再试一次。
基本上,我们的想法是只用 1 个运算符找到最高优先级的表达式,对其求值,然后用它替换。这是一些伪代码:
input = [user input]
while expression still contains (, ), +, -, *, or /:
toEvaluate = highest priority expression with 1 operator (e.g. 1+2, or 2*3)
calculate the decimal value of toEvaluate
modify input so that you replace toEvaluate with its decimal value
请注意,在您的 doMath()
实现中,在您的 for 循环中,您只需在看到所有运算符时立即对其进行评估。例如,考虑
1+2*3
您将首先看到 +,计算 1+2,然后将结果乘以 3。相反,您需要先遍历整个列表,找到优先级最高的运算符,对其求值,然后从头开始再次.
既然你要将它输入到一个字符串中,为什么不制作一个遍历每个字符的 for 循环?
// This for loop will get you the string with only one operator in it.
// EX: 30+32
// You will have to find some way to compute this string.
for(int i = 0; i < userInput.length(); i++)
{
//int a will be the INDEX of the first operator
//int b will be the INDEX of the place where a new operator comes up
char c = userInput.getCharAt(i);
a = 0;
//I'm just gonna do it with + and - for now. Add any other operators.
if(c == '+' || c == '-')
{
b = i;
String stringToCompute = userInput.substring(a,b);
//Find some way to take the string there then compute it.
//Maybe another for loop with stringToCompute to find where the
// operator is and then add/subtract the two doubles.
// Now reset it
a = i;
b = null;
}
}
这里有几个可能的设计来解决这个问题:
您可以将处理分为两部分:解析和求值。在解析阶段,您将字符串转换为表示将如何对其进行评估的数据结构。在评估阶段,您遍历树并对表达式进行评估。
您可以按运算符拆分列表以评估最后(加号和减号),评估每个段(乘法和除法)。如果设计得当,这可能是递归的。
第一个选项更好,因为它更可扩展,但实施起来也更费力。这是通过解析和评估生成的示例数据结构:
interface Term {
double getValue();
}
enum Operator {
MULTIPLY, DIVIDE, ADD, SUBTRACT;
double getValue(List<Double> operands) {
...
}
}
class Operation implements Term {
List<Term> operands;
Operator operator;
double getValue() {
return operator.getValue(operands);
}
}
class Constant implements Term {
private final double value;
double getValue() {
return value;
}
}