4

我正在写一个计算器,当用户按下回车键时,我需要它来找到任何有括号的地方。

然后我需要计算器先解决其中的数学问题。

获取括号内的函数并将秒的值设置为括号内的值的最佳方法是String什么?

4

5 回答 5

3

您可以使用堆栈:

  1. 将字符压入堆栈直到)
  2. 然后让你的子方程从堆栈中弹出,直到(
  3. 将步骤 2 的方程结果推入堆栈
  4. 重复直到找不到括号,然后求解最终方程
于 2013-09-26T21:48:23.600 回答
2

作为替代方案,您可以让 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 解决方案。

于 2013-09-26T23:08:54.220 回答
1

你有一些不同的方法来解决这个问题,

  1. 定义一个全局变量,当用户按下其中一个操作数(+、-、*/)时,您将其保存在定义变量中。
  2. 拆分字符串,如果您将整个输入视为一个字符串,您可以使用定义的操作数(+、-、*/)之一将其拆分,然后您将拥有一组运算符和操作数。
  3. 使用正则表达式,您可以使用正则表达式找出哪些运算符和操作数。
于 2013-09-26T21:46:04.507 回答
0

我喜欢前两张海报的想法,所以这是我的方法:

1) 将字符放入堆栈。当您看到 a 时,)您会从堆栈中弹出成员,直到看到(. 完成后,弹出所有字符并将它们以相反的顺序放入另一个 String 或 StringBuffer 或 StringBuilder 中。

2) 按相反的优先级拆分等式的其余部分。然后对于每个更高级别的运算符优先级,您按该运算符拆分子方程。

3) 然后,当您处于最高级别(例如指数)时,您就可以解决所有这些问题。然后进行较低级别的操作员。

于 2013-09-26T22:30:46.110 回答
0

这是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();
  }
于 2013-09-27T04:40:49.583 回答