我发现我明显缺乏RPN计算器,所以我决定自己制作。我仍然在向它添加新的函数,但从现在开始,我想重构我的代码。
因为这是一个小问题,而且我还没有为它制作GUI,所以我只使用一个类。
package owl;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.text.DecimalFormat;
import java.util.Stack;
public class Main {
public static void main(String[] args) {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
Stack<Double> stack = new Stack<Double>();
System.out.println("JavaRPN: Input numbers and operands separated by newline or space");
DecimalFormat df = new DecimalFormat("#,###.#########");
while (true) {
String input = reader.readLine();
String[] inputs = input.split(" ");
for (int i = 0; i < inputs.length; i++) {
if (isNumber(inputs[i])) {
stack.push(Double.parseDouble(inputs[i]));
continue;
}
if (inputs[i].equals("e") || inputs[i].equals("p") || inputs[i].equals("c")) {
commands(inputs[i], stack, df);
} else if (inputs[i].equals("sq") || inputs[i].equals("sin") || inputs[i].equals("cos")
|| inputs[i].equals("tan") || inputs[i].equals("asin") || inputs[i].equals("acos")
|| inputs[i].equals("atan")) {
function(inputs[i], stack);
} else if (inputs[i].equals("+") || inputs[i].equals("-") || inputs[i].equals("*")
|| inputs[i].equals("/") || inputs[i].equals("^")) {
operator(stack, inputs[i]);
} else {
System.out.println("ERROR: Invalid input");
}
}
}
} catch (
Exception e) {
e.printStackTrace();
}
}
private static void commands(String input, Stack<Double> stack, DecimalFormat df) {
switch (input) {
case "e":
System.exit(0);
;
break;
case "p":
if (stack.size() > 0) {
System.out.println(df.format(stack.peek()));
break;
} else {
System.out.println("ERROR: All Stacks Empty");
break;
}
case "c":
stack.clear();
break;
}
}
private static void function(String string, Stack<Double> stack) {
if (stack.size() > 0) {
double num = stack.pop();
switch (string) {
case "sq":
stack.push(num * num);
break;
case "sin":
stack.push(Math.sin(Math.toRadians(num)));
break;
case "cos":
stack.push(Math.cos(Math.toRadians(num)));
break;
case "tan":
stack.push(Math.tan(Math.toRadians(num)));
break;
case "asin":
stack.push(Math.asin(Math.toRadians(num)));
break;
case "acos":
stack.push(Math.acos(Math.toRadians(num)));
break;
case "atan":
stack.push(Math.atan(Math.toRadians(num)));
break;
}
}
}
private static void operator(Stack<Double> stack, String input) {
if (stack.size() > 1) {
double num2 = stack.pop();
double num1 = stack.pop();
switch (input) {
case "+":
stack.push(num1 + num2);
break;
case "-":
stack.push(num1 - num2);
break;
case "*":
stack.push(num1 * num2);
break;
case "/":
stack.push(num1 / num2);
break;
case "^":
stack.push(Math.pow(num1, num2));
break;
}
} else {
System.out.println("ERROR: Can't operate on an empty stack");
}
}
private static boolean isNumber(String input) {
try {
Double.parseDouble(input);
return true;
} catch (Exception e) {
return false;
}
}
}任何熟悉RPN计算器和UNIX系统的人都可能熟悉内置的dc计算器。计算器解析输入非常类似于旧程序,但支持小数与旧程序不同(或者至少我找不到在其中使用小数的方法)。
我知道,在代码执行之前检查输入的行中,我显然是多余的,该方法检查相同的精确方法。我以前曾尝试过重构,但我想不出如何在不破坏当前代码的情况下更有效地完成这一任务。
我经常在我的github上更新这个项目:https://github.com/ViceroyFaust/JavaRPN/tree/Refactoring ^我做了更新,请告诉我我是改进了还是使问题变得更糟了^_^
发布于 2019-08-26 09:43:10
将所有代码放入一个类中会使程序变得非常复杂。将代码重构为每个类执行一个任务的类。这叫做单一责任原则
RpnEgine的核心是以下功能。我使用了Deque,因为它比同步堆栈性能更好。
public void process(String ... input) {
for (String s: input) {
final Consumer<Deque<Double>> func = FUNCTIONS.get(s);
if (func != null) {
func.accept(stack);
} else {
stack.push(Double.valueOf(s));
}
}
}然后,数学操作和命令可以定义为lambdas或独立类。它们有点难看,因为从堆栈读取时,操作符顺序被颠倒了。您会注意到这段代码经常重复推送和弹出,所以最好将它们重构到一个检查堆栈大小、弹出操作数、将它们委托给BiFunction并推送结果的公共类。
它还引入了很大的灵活性,因为实现计算堆栈中任何内容之和的函数变得非常简单。
static {
FUNCTIONS.put("+", (d) -> d.push(d.pop() + d.pop()));
FUNCTIONS.put("-", (d) -> d.push((- d.pop()) + d.pop()));
FUNCTIONS.put("/", (d) -> d.push(1.0 / (d.pop() / d.pop())));
FUNCTIONS.put("sum", new Sum());
}不要将输入解析放到同一个类中。创建一个名为RpnCli的类,该类读取输入并将其传递给RpnEngine。
发布于 2021-12-02 20:01:20
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Map;
import java.util.Stack;
import java.util.function.DoubleBinaryOperator;
import java.util.function.DoubleUnaryOperator;
import static java.util.Map.entry;
public class Rpn {
private static final Map<String, DoubleUnaryOperator> FUNCTIONS = Map.ofEntries(
entry("sin", Math::sin),
entry("cos", Math::cos),
entry("tan", Math::tan)
);
private static final Map<String, DoubleBinaryOperator> OPERATORS = Map.ofEntries(
entry("+", (n1, n2) -> n1 + n2),
entry("-", (n1, n2) -> n1 - n2),
entry("*", (n1, n2) -> n1 * n2),
entry("/", (n1, n2) -> n1 / n2),
entry("^", Math::pow)
);
public static void main(String[] args) throws Exception {
System.out.println("Rpn: Reverse Polish Notation Calculator");
final var reader = new BufferedReader(new InputStreamReader(System.in));
final var stack = new Stack<Double>();
var valid = true;
String line;
while (valid && ((line = reader.readLine()) != null)) {
for (final var token : line.split("\\s+")) {
valid =
applyNumber(token, stack) ||
applyFunction(token, stack) ||
applyOperator(token, stack);
}
}
reader.close();
}
private static boolean applyFunction(final String token, final Stack<Double> stack) {
if (!stack.isEmpty()) {
final var f = FUNCTIONS.get(token);
if (f != null) {
stack.push(f.applyAsDouble(Math.toRadians(stack.pop())));
return true;
}
}
return false;
}
private static boolean applyOperator(final String token, final Stack<Double> stack) {
if (stack.size() > 1) {
final var op = OPERATORS.get(token);
if (op != null) {
final var num2 = stack.pop();
final var num1 = stack.pop();
stack.push(op.applyAsDouble(num1, num2));
return true;
}
}
return false;
}
private static boolean applyNumber(final String token, final Stack<Double> stack) {
try {
stack.push(Double.parseDouble(token));
return true;
}
catch (final Exception e) {
return false;
}
}
}为了简洁起见,删除了“命令”,但以下概念通常适用:
println分开)。while(true)。\\s+允许在多个空格之间拆分)?null检查API调用可以返回null的位置来验证数据。final。FUNCTIONS和OPERATORS)。var。最后,使用现代IDE进行开发,这将产生显示代码改进的警告。例如,IntelliJ将显示stack.size() != 0等同于!stack.isEmpty(),以及可以是switch的if语句,以及更多的简化。
https://codereview.stackexchange.com/questions/226782
复制相似问题