Существует множество способов сделать это, и важно выбрать правильный. Вероятно, одним из самых больших архитектурных решений является то, как код модели будет доступен или доступен во всем приложении.
Я написал сообщение в блоге об этом некоторое время назад: Общий код модели , Ниже приведен краткий обзор:
. Один из подходов состоит в том, чтобы совместно использовать указатели на объекты модели между контроллерами представления.
Поскольку подготовка к segue является наиболее распространенной, здесь приведен пример:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var next = segue.destinationViewController as NextViewController
next.dataSource = dataSource
}
Другой подход заключается в том, чтобы обрабатывать полный экран данных одновременно, и вместо того, чтобы связывать контроллеры представлений друг с другом, каждый из них контролирует один источник данных, который они могут получить независимо.
Наиболее распространенный способ, которым я видел это, - это экземпляр singleton . Поэтому, если ваш объект singleton был DataAccess
, вы можете сделать следующее в методе viewDidLoad для UIViewController:
func viewDidLoad() {
super.viewDidLoad()
var data = dataAccess.requestData()
}
. Существуют дополнительные инструменты, которые также помогают передавать данные:
Хорошая вещь в Core Data заключается в том, что она имеет обратные отношения. Поэтому, если вы хотите просто предоставить NotesViewController объект заметок, который вы можете использовать, поскольку он будет иметь обратную связь с чем-то другим, например, с ноутбуком. Если вам нужны данные на ноутбуке в NotesViewController, вы можете вернуться к графику объекта, выполнив следующие действия:
let notebookName = note.notebook.name
Подробнее об этом читайте в моем сообщении в блоге: Общий код модели
С JDK1.6 вы можете использовать встроенный механизм Javascript.
import javax.script.ScriptEngineManager;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
public class Test {
public static void main(String[] args) throws ScriptException {
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("JavaScript");
String foo = "40+2";
System.out.println(engine.eval(foo));
}
}
public class StringCalculator {
public static void main(String[] args) {
String eval = "2+3*2/2+2*5/5*5";
System.out.println(calculator(eval));
}
public static int calcMulAndDiv(String val){
String nos[] = val.split("\\D+");
String opr[] = val.split("\\d+");
int res = Integer.parseInt(nos[0]);
for(int i = 1; i< opr.length ;i++){
if(opr[i].equals("*")){
res = res * Integer.parseInt(nos[i]);
}
else if(opr[i].equals("/")){
res = res / Integer.parseInt(nos[i]);
}
}
return res;
}
public static int calculator(String val){
String nos[] = val.split("[+-]");
String operators = val.replaceAll("[^+-]","");
char opr[] = operators.toCharArray();
int result = 0;
if(nos[0].contains("*") || nos[0].contains("*")){
result = calcMulAndDiv(nos[0]);
}else{
result = Integer.parseInt(nos[0]);
}
for(int i = 0 ; i < opr.length ; i++){
if(opr[i] == '+'){
if(nos[i+1].contains("*") || nos[i+1].contains("*")){
result = result + calcMulAndDiv(nos[i+1]);
}else{
result = result + Integer.parseInt(nos[i+1]);
}
}
else if(opr[i] == '-'){
if(nos[i+1].contains("*") || nos[i+1].contains("*")){
result = result + calcMulAndDiv(nos[i+1]);
}else{
result = result - Integer.parseInt(nos[i+1]);
}
}
}
return result;
}
}
Я написал этот метод eval
для арифметических выражений, чтобы ответить на этот вопрос. Это добавление, вычитание, умножение, деление, возведение в степень (использование символа ^
) и несколько основных функций, таких как sqrt
. Он поддерживает группировку с использованием (
... )
и получает правильные правила приоритета и ассоциативности .
public static double eval(final String str) {
return new Object() {
int pos = -1, ch;
void nextChar() {
ch = (++pos < str.length()) ? str.charAt(pos) : -1;
}
boolean eat(int charToEat) {
while (ch == ' ') nextChar();
if (ch == charToEat) {
nextChar();
return true;
}
return false;
}
double parse() {
nextChar();
double x = parseExpression();
if (pos < str.length()) throw new RuntimeException("Unexpected: " + (char)ch);
return x;
}
// Grammar:
// expression = term | expression `+` term | expression `-` term
// term = factor | term `*` factor | term `/` factor
// factor = `+` factor | `-` factor | `(` expression `)`
// | number | functionName factor | factor `^` factor
double parseExpression() {
double x = parseTerm();
for (;;) {
if (eat('+')) x += parseTerm(); // addition
else if (eat('-')) x -= parseTerm(); // subtraction
else return x;
}
}
double parseTerm() {
double x = parseFactor();
for (;;) {
if (eat('*')) x *= parseFactor(); // multiplication
else if (eat('/')) x /= parseFactor(); // division
else return x;
}
}
double parseFactor() {
if (eat('+')) return parseFactor(); // unary plus
if (eat('-')) return -parseFactor(); // unary minus
double x;
int startPos = this.pos;
if (eat('(')) { // parentheses
x = parseExpression();
eat(')');
} else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers
while ((ch >= '0' && ch <= '9') || ch == '.') nextChar();
x = Double.parseDouble(str.substring(startPos, this.pos));
} else if (ch >= 'a' && ch <= 'z') { // functions
while (ch >= 'a' && ch <= 'z') nextChar();
String func = str.substring(startPos, this.pos);
x = parseFactor();
if (func.equals("sqrt")) x = Math.sqrt(x);
else if (func.equals("sin")) x = Math.sin(Math.toRadians(x));
else if (func.equals("cos")) x = Math.cos(Math.toRadians(x));
else if (func.equals("tan")) x = Math.tan(Math.toRadians(x));
else throw new RuntimeException("Unknown function: " + func);
} else {
throw new RuntimeException("Unexpected: " + (char)ch);
}
if (eat('^')) x = Math.pow(x, parseFactor()); // exponentiation
return x;
}
}.parse();
}
Пример :
System.out.println(eval("((4 - 2^3 + 1) * -sqrt(3*3+4*4)) / 2"));
Выход: 7.5 (что правильно)
Парсер является рекурсивным парсером спуска , поэтому внутренне использует отдельные методы анализа для каждого уровня приоритета оператора в своей грамматике. Я сохранил его, поэтому его легко модифицировать, но вот несколько идей, которые вы можете расширить, с помощью:
eval
, например Map<String,Double> variables
. @FunctionalInterface
interface Expression {
double eval();
}
Теперь измените все методы, возвращающие double
s, поэтому вместо этого они возвращают экземпляр этого интерфейса. Синтаксис лямбды Java 8 отлично подходит для этого. Пример одного из измененных методов: Expression parseExpression() {
Expression x = parseTerm();
for (;;) {
if (eat('+')) { // addition
Expression a = x, b = parseTerm();
x = (() -> a.eval() + b.eval());
} else if (eat('-')) { // subtraction
Expression a = x, b = parseTerm();
x = (() -> a.eval() - b.eval());
} else {
return x;
}
}
}
Создает рекурсивное дерево объектов Expression
, представляющих скомпилированное выражение (дерево синтаксиса ). Затем вы можете скомпилировать его один раз и повторно оценить его разными значениями: public static void main(String[] args) {
Map<String,Double> variables = new HashMap<>();
Expression exp = parse("x^2 - x + 2", variables);
for (double x = -20; x <= +20; x++) {
variables.put("x", x);
System.out.println(x + " => " + exp.eval());
}
}
double
вы можете изменить оценщика, чтобы использовать что-то более мощное, например BigDecimal
, или класс, который реализует комплексные числа или рациональные числа (дроби). Вы даже можете использовать Object
, позволяя смешивать типы данных в выражениях, как и в реальном языке программирования. :) Весь код в этом ответе опубликовал в общедоступном домене . Получайте удовольствие!
В этой статье указаны три разных подхода, один из которых - JEXL из Apache и позволяет сценарии, содержащие ссылки на java-объекты.
Попробуйте использовать следующий пример кода, используя Javascript-механизм JDK1.6 с обработкой ввода кода.
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
public class EvalUtil {
private static ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
public static void main(String[] args) {
try {
System.out.println((new EvalUtil()).eval("(((5+5)/2) > 5) || 5 >3 "));
System.out.println((new EvalUtil()).eval("(((5+5)/2) > 5) || true"));
} catch (Exception e) {
e.printStackTrace();
}
}
public Object eval(String input) throws Exception{
try {
if(input.matches(".*[a-zA-Z;~`#$_{}\\[\\]:\\\\;\"',\\.\\?]+.*")) {
throw new Exception("Invalid expression : " + input );
}
return engine.eval(input);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
}
Я думаю, что когда-либо вы это сделаете, это будет связано с множеством условных утверждений. Но для отдельных операций, например, в ваших примерах, вы можете ограничить это до 4 операторов с чем-то вроде
String math = "1+4";
if (math.split("+").length == 2) {
//do calculation
} else if (math.split("-").length == 2) {
//do calculation
} ...
. Это становится намного сложнее, если вы хотите иметь дело с несколькими операциями типа «4 + 5 * 6 ".
Если вы пытаетесь построить калькулятор, я бы перегружал каждую секцию вычисления отдельно (каждый номер или оператор), а не как одну строку.
Это фактически дополняет ответ, данный @Boann. У этого есть небольшая ошибка, которая вызывает «-2 ^ 2», чтобы дать ошибочный результат -4.0. Проблема для этого - точка, в которой в его оценке оценивается степень возведения в степень. Просто переместите экспоненцию в блок parseTerm (), и все будет хорошо. Посмотрите ниже, что @ Ответ Боанна слегка изменен. Модификация содержится в комментариях.
public static double eval(final String str) {
return new Object() {
int pos = -1, ch;
void nextChar() {
ch = (++pos < str.length()) ? str.charAt(pos) : -1;
}
boolean eat(int charToEat) {
while (ch == ' ') nextChar();
if (ch == charToEat) {
nextChar();
return true;
}
return false;
}
double parse() {
nextChar();
double x = parseExpression();
if (pos < str.length()) throw new RuntimeException("Unexpected: " + (char)ch);
return x;
}
// Grammar:
// expression = term | expression `+` term | expression `-` term
// term = factor | term `*` factor | term `/` factor
// factor = `+` factor | `-` factor | `(` expression `)`
// | number | functionName factor | factor `^` factor
double parseExpression() {
double x = parseTerm();
for (;;) {
if (eat('+')) x += parseTerm(); // addition
else if (eat('-')) x -= parseTerm(); // subtraction
else return x;
}
}
double parseTerm() {
double x = parseFactor();
for (;;) {
if (eat('*')) x *= parseFactor(); // multiplication
else if (eat('/')) x /= parseFactor(); // division
else if (eat('^')) x = Math.pow(x, parseFactor()); //exponentiation -> Moved in to here. So the problem is fixed
else return x;
}
}
double parseFactor() {
if (eat('+')) return parseFactor(); // unary plus
if (eat('-')) return -parseFactor(); // unary minus
double x;
int startPos = this.pos;
if (eat('(')) { // parentheses
x = parseExpression();
eat(')');
} else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers
while ((ch >= '0' && ch <= '9') || ch == '.') nextChar();
x = Double.parseDouble(str.substring(startPos, this.pos));
} else if (ch >= 'a' && ch <= 'z') { // functions
while (ch >= 'a' && ch <= 'z') nextChar();
String func = str.substring(startPos, this.pos);
x = parseFactor();
if (func.equals("sqrt")) x = Math.sqrt(x);
else if (func.equals("sin")) x = Math.sin(Math.toRadians(x));
else if (func.equals("cos")) x = Math.cos(Math.toRadians(x));
else if (func.equals("tan")) x = Math.tan(Math.toRadians(x));
else throw new RuntimeException("Unknown function: " + func);
} else {
throw new RuntimeException("Unexpected: " + (char)ch);
}
//if (eat('^')) x = Math.pow(x, parseFactor()); // exponentiation -> This is causing a bit of problem
return x;
}
}.parse();
}
Вы можете легко оценивать выражения, если ваше приложение Java уже обращается к базе данных без использования каких-либо других JAR.
Некоторые базы данных требуют, чтобы вы использовали фиктивную таблицу (например, «двойную» таблицу Oracle) и другие позволит вам оценивать выражения без «выбора» из любой таблицы.
Например, в Sql Server или Sqlite
select (((12.10 +12.0))/ 233.0) amount
и в Oracle
select (((12.10 +12.0))/ 233.0) amount from dual;
Преимущество использования БД состоит в том, что вы можете одновременно оценить множество выражений. Кроме того, большинство БД позволит вам использовать очень сложные выражения и также будет иметь ряд дополнительных функций, которые можно назвать необходимыми.
Однако производительность может пострадать, если много отдельных выражений нужно оценивать индивидуально, особенно когда БД находится на сетевом сервере.
Ниже рассматриваются проблемы производительности в некоторой степени, используя базу данных Sqlite in-memory.
Вот полный рабочий пример в Java
Class. forName("org.sqlite.JDBC");
Connection conn = DriverManager.getConnection("jdbc:sqlite::memory:");
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery( "select (1+10)/20.0 amount");
rs.next();
System.out.println(rs.getBigDecimal(1));
stat.close();
conn.close();
Конечно, вы могли бы расширить вышеупомянутый код, чтобы обрабатывать несколько вычислений одновременно.
ResultSet rs = stat.executeQuery( "select (1+10)/20.0 amount, (1+100)/20.0 amount2");
import java.util.*;
StringTokenizer st;
int ans;
public class check {
String str="7 + 5";
StringTokenizer st=new StringTokenizer(str);
int v1=Integer.parseInt(st.nextToken());
String op=st.nextToken();
int v2=Integer.parseInt(st.nextToken());
if(op.equals("+")) { ans= v1 + v2; }
if(op.equals("-")) { ans= v1 - v2; }
//.........
}
Можно преобразовать любую строку выражения в нотации infix в постфиксную нотацию, используя алгоритм shing-yard Джикстры . Результат алгоритма затем может служить входом в постфиксный алгоритм с возвратом результата выражения.
Здесь я написал статью об этом , с реализацией в java
Еще один способ - использовать Spring Expression Language или SpEL, который делает намного больше, а также оценивает математические выражения, поэтому может немного переборщить. Вам не нужно использовать Spring framework для использования этой библиотеки выражений, поскольку она является автономной. Копирование примеров из документации SpEL:
ExpressionParser parser = new SpelExpressionParser();
int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2
double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); //24.0
Ниже приведены более сжатые примеры Spel здесь и полные документы здесь
Правильный способ решить это с помощью lexer и синтаксического анализатора . Вы можете написать простые версии из них самостоятельно, или эти страницы также имеют ссылки на Java-лексеры и парсеры.
Создание рекурсивного синтаксического анализа является хорошим упражнением.
Для моего университетского проекта я искал анализатор / оценщик, поддерживающий как основные формулы, так и более сложные уравнения (особенно повторяющиеся операторы). Я нашел очень хорошую библиотеку с открытым исходным кодом для JAVA и .NET под названием mXparser.
http://mathparser.org/ / g4] http://mathparser.org/mxparser-tutorial/ И несколько примеров 1 - Простая механика 2 - Пользовательские аргументы и константы 3 - Пользователь определенные функции 4 - Итерация С наилучшими пожеланиями Expression e = new Expression("( 2 + 3/4 + sin(pi) )/2");
double v = e.calculate()
Argument x = new Argument("x = 10");
Constant a = new Constant("a = pi^2");
Expression e = new Expression("cos(a*x)", x, a);
double v = e.calculate()
Function f = new Function("f(x, y, z) = sin(x) + cos(y*z)");
Expression e = new Expression("f(3,2,5)", f);
double v = e.calculate()
Expression e = new Expression("sum( i, 1, 100, sin(i) )");
double v = e.calculate()
Как насчет чего-то вроде этого:
String st = "10+3";
int result;
for(int i=0;i<st.length();i++)
{
if(st.charAt(i)=='+')
{
result=Integer.parseInt(st.substring(0, i))+Integer.parseInt(st.substring(i+1, st.length()));
System.out.print(result);
}
}
и выполните аналогичные действия для каждого другого математического оператора соответственно.
Вы можете взглянуть на структуру Symja :
ExprEvaluator util = new ExprEvaluator();
IExpr result = util.evaluate("10-40");
System.out.println(result.toString()); // -> "-30"
Обратите внимание, что можно определить более сложные выражения:
// D(...) gives the derivative of the function Sin(x)*Cos(x)
IAST function = D(Times(Sin(x), Cos(x)), x);
IExpr result = util.evaluate(function);
// print: Cos(x)^2-Sin(x)^2
Для запуска javascript можно использовать внешнюю библиотеку, такую как RHINO или NASHORN. И javascript может оценивать простую формулу без парсинга строки. Не влияет на производительность, если код написан хорошо. Ниже приведен пример с RHINO -
public class RhinoApp {
private String simpleAdd = "(12+13+2-2)*2+(12+13+2-2)*2";
public void runJavaScript() {
Context jsCx = Context.enter();
Context.getCurrentContext().setOptimizationLevel(-1);
ScriptableObject scope = jsCx.initStandardObjects();
Object result = jsCx.evaluateString(scope, simpleAdd , "formula", 0, null);
Context.exit();
System.out.println(result);
}
Вы также можете попробовать интерпретатор BeanShell :
Interpreter interpreter = new Interpreter();
interpreter.eval("result = (7+21*6)/(32-27)");
System.out.println(interpreter.get("result"));
Класс Java, который может оценивать математические выражения:
package test;
public class Calculator {
public static Double calculate(String expression){
if (expression == null || expression.length() == 0) {
return null;
}
return calc(expression.replace(" ", ""));
}
public static Double calc(String expression) {
if (expression.startsWith("(") && expression.endsWith(")")) {
return calc(expression.substring(1, expression.length() - 1));
}
String[] containerArr = new String[]{expression};
double leftVal = getNextOperand(containerArr);
expression = containerArr[0];
if (expression.length() == 0) {
return leftVal;
}
char operator = expression.charAt(0);
expression = expression.substring(1);
while (operator == '*' || operator == '/') {
containerArr[0] = expression;
double rightVal = getNextOperand(containerArr);
expression = containerArr[0];
if (operator == '*') {
leftVal = leftVal * rightVal;
} else {
leftVal = leftVal / rightVal;
}
if (expression.length() > 0) {
operator = expression.charAt(0);
expression = expression.substring(1);
} else {
return leftVal;
}
}
if (operator == '+') {
return leftVal + calc(expression);
} else {
return leftVal - calc(expression);
}
}
private static double getNextOperand(String[] exp){
double res;
if (exp[0].startsWith("(")) {
int open = 1;
int i = 1;
while (open != 0) {
if (exp[0].charAt(i) == '(') {
open++;
} else if (exp[0].charAt(i) == ')') {
open--;
}
i++;
}
res = calc(exp[0].substring(1, i - 1));
exp[0] = exp[0].substring(i);
} else {
int i = 1;
if (exp[0].charAt(0) == '-') {
i++;
}
while (exp[0].length() > i && isNumber((int) exp[0].charAt(i))) {
i++;
}
res = Double.parseDouble(exp[0].substring(0, i));
exp[0] = exp[0].substring(i);
}
return res;
}
private static boolean isNumber(int c) {
int zero = (int) '0';
int nine = (int) '9';
return (c >= zero && c <= nine) || c =='.';
}
public static void main(String[] args) {
System.out.println(calculate("(((( -6 )))) * 9 * -1"));
System.out.println(calc("(-5.2+-5*-5*((5/4+2)))"));
}
}
, если мы собираемся его реализовать, мы можем использовать приведенный ниже алгоритм: -
1 While the thing on top of the operator stack is not a
left parenthesis,
1 Pop the operator from the operator stack.
2 Pop the value stack twice, getting two operands.
3 Apply the operator to the operands, in the correct order.
4 Push the result onto the value stack.
2 Pop the left parenthesis from the operator stack, and discard it.
1.2.5 Оператор (назовите его thisOp): 1 While the operator stack is not empty, and the top thing on the
operator stack has the same or greater precedence as thisOp,
1 Pop the operator from the operator stack.
2 Pop the value stack twice, getting two operands.
3 Apply the operator to the operands, in the correct order.
4 Push the result onto the value stack.
2 Push thisOp onto the operator stack.
Слишком поздно, чтобы ответить, но я столкнулся с такой же ситуацией, чтобы оценить выражение в java, это может помочь кому-то
MVEL
выполнять вычисления времени выполнения, мы можем написать java-код в String
чтобы получить его в этом.
String expressionStr = "x+y";
Map<String, Object> vars = new HashMap<String, Object>();
vars.put("x", 10);
vars.put("y", 20);
ExecutableStatement statement = (ExecutableStatement) MVEL.compileExpression(expressionStr);
Object result = MVEL.executeExpression(statement, vars);
Это еще одна интересная альтернатива https://github.com/Shy-Ta/expression-evaluator-demo
. Использование очень простое и выполняет задание, например:
ExpressionsEvaluator evalExpr = ExpressionsFactory.create("2+3*4-6/2");
assertEquals(BigDecimal.valueOf(11), evalExpr.eval());
Еще один вариант: https://github.com/stefanhaustein/expressionparser
Я применил это, чтобы иметь простой, но гибкий вариант, позволяющий обоим:
Связанный выше TreeBuilder является частью демонстрационного пакета CAS , который выполняет символический вывод. Существует также пример BASIC-интерпретатора , и я начал использовать интерпретатор TypeScript , используя его.
ЗДЕСЬ - это другая библиотека с открытым исходным кодом на GitHub с именем EvalEx.
В отличие от механизма JavaScript эта библиотека ориентирована только на оценку математических выражений. Кроме того, библиотека расширяема и поддерживает использование логических операторов, а также круглых скобок.