我正在写一个计算器,当用户按下回车键时,我需要它来找到任何有括号的地方。
然后我需要计算器先解决其中的数学问题。
获取括号内的函数并将秒的值设置为括号内的值的最佳方法是String
什么?
您可以使用堆栈:
)
(
作为替代方案,您可以让 Java 自行编译。它仅限于简单的操作,例如+ - / *
完整的工作示例:
import java.lang.*;
import java.io.*;
public class SO {
public static void main(String[] args) {
try { System.out.println(calculate("5 * (1 + 1)")); }
catch (Exception e) { System.out.println("we tried " + e); }
}
private static String subProc(String command) throws Exception {
Process proc = Runtime.getRuntime().exec(command); // kick off sub process
BufferedReader stdout = new BufferedReader(new
InputStreamReader(proc.getInputStream())); // read from stdout
StringBuilder sb = new StringBuilder(); // build output
String ln = stdout.readLine(); // read lines, until we are at the end
while (ln != null) { sb.append(ln); ln = stdout.readLine(); }
proc.waitFor(); // wait for process to exit
int exitCode = proc.exitValue(); // get exit code
if (exitCode != 0) // if it isn't 0, something went wrong. Throw error
throw new Exception("invalid math! exited with code: " + exitCode);
return sb.toString(); // return stdout
}
private static String calculate(String math) throws Exception {
//** Compile a new Java class that will spit out the calculation
File file = File.createTempFile("tmp", ".java"); // create new temp file
String classpath = file.getParent(), // get class path, and name
classname = file.getName().substring(0, file.getName().length() - 5);
PrintWriter writer = new PrintWriter(file); // write Java to temp file
writer.println(
"public class " + classname + "{" +
"public static void main(String[] args) { " +
"System.out.println(" + math + "); }}"); writer.close();
subProc("javac " + file.getAbsolutePath()); // compile it
file.delete(); // remove our source file
return subProc("java -cp " + classpath + " " + classname); // run it
}
}
像往常一样编译和运行:
javac SO.java; java SO
在这个特定的例子中,它将打印出来10
,因为调用是calculate("5 * (1 + 1)"));
从速度上看,这不是解决问题的实用方法,但它是我喜欢的纯 Java 解决方案。
你有一些不同的方法来解决这个问题,
我喜欢前两张海报的想法,所以这是我的方法:
1) 将字符放入堆栈。当您看到 a 时,)
您会从堆栈中弹出成员,直到看到(
. 完成后,弹出所有字符并将它们以相反的顺序放入另一个 String 或 StringBuffer 或 StringBuilder 中。
2) 按相反的优先级拆分等式的其余部分。然后对于每个更高级别的运算符优先级,您按该运算符拆分子方程。
3) 然后,当您处于最高级别(例如指数)时,您就可以解决所有这些问题。然后进行较低级别的操作员。
这是Wikipedia page on recursive descent parser 上语法子集的递归下降解析器,似乎与您的案例相关。
我没有包含分词器,但编写一个适合界面的分词器应该相当简单。该代码与 Wikipedia 页面上的代码没有显着不同,只是只实现了语法的一个子集,并且它实际上执行了计算。
/**
* From http://en.wikipedia.org/wiki/Recursive_descent_parser
*
* expression =
* [ "+" | "-" ] term { ("+" | "-") term } .
*
* term =
* factor { ( "*" | "/" ) factor } .
*
* factor =
* number
* | "(" expression ")" .
*/
public class Arithmetic
{
private final TokenStream tokenStream;
private TokenStream.Token currentToken;
private double currentValue;
public Arithmetic(TokenStream tokenStream) {
this.tokenStream = tokenStream;
}
public double parse() {
nextToken();
return expression();
}
private double expression() {
double lhs = 0.0;
if (accept(TokenStream.Token.MINUS)) {
lhs = -term();
} else {
// Optional unary plus swallowed
accept(TokenStream.Token.PLUS);
lhs = term();
}
for (boolean moreTerms = true; moreTerms; ) {
if (accept(TokenStream.Token.PLUS)) {
lhs += term();
} else if (accept(TokenStream.Token.MINUS)) {
lhs -= term();
} else {
moreTerms = false;
}
}
return lhs;
}
private double term() {
double lhs = factor();
for (boolean moreFactors = true; moreFactors; ) {
if (accept(TokenStream.Token.TIMES)) {
lhs *= factor();
} else if (accept(TokenStream.Token.DIVIDED_BY)) {
lhs /= factor();
} else {
moreFactors = false;
}
}
return lhs;
}
private double factor() {
if (peek(TokenStream.Token.NUMBER)) {
// Save currentValue before calling nextToken()
double value = currentValue;
nextToken();
return value;
}
require(TokenStream.Token.OPEN);
double value = expression();
require(TokenStream.Token.CLOSE);
return value;
}
private void nextToken() {
currentToken = tokenStream.nextToken();
if (currentToken == TokenStream.Token.NUMBER) {
currentValue = tokenStream.getValue();
}
}
private boolean peek(TokenStream.Token token) {
return (currentToken == token);
}
private boolean accept(TokenStream.Token token) {
if (peek(token)) {
nextToken();
return true;
}
return false;
}
private void require(TokenStream.Token token) {
if (currentToken == token) {
nextToken();
} else {
throw new IllegalStateException("Unexpected token " + currentToken);
}
}
}
的界面TokenStream
非常简单:
public interface TokenStream {
public enum Token {
// Terminals
PLUS,
MINUS,
TIMES,
DIVIDED_BY,
OPEN,
CLOSE,
NUMBER,
EOF
}
Token nextToken();
// Retrieve value when nextToken() returns NUMBER
double getValue();
}
另一种可能性是使用脚本工具来评估一些 JavaScript,如下所示:
try
{
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("JavaScript");
Object result = engine.eval("1 + 2 / 2");
System.out.println(result.getClass().getCanonicalName());
System.out.println(result);
}
catch (ScriptException e)
{
e.printStackTrace();
}