Avaliando cadeia “3 * (4 + 2)” um rendimento de 18 int [duplicado]
Pergunta
Esta questão já tem uma resposta aqui:
Existe uma função do .NET framework que pode avaliar uma expressão numérica contida em uma corda e retornar o resultado? F.E.:.
string mystring = "3*(2+4)";
int result = EvaluateExpression(mystring);
Console.Writeln(result); // Outputs 18
Existe uma função estrutura padrão que você pode substituir o meu método EvaluateExpression
com?
Solução
Sim, você pode deixar compilador C # avaliá-lo em tempo de execução.
Veja: CSharpCorner
Outras dicas
Se você quiser avaliar um uso expressão de cadeia o abaixo trecho de código.
using System.Data;
DataTable dt = new DataTable();
var v = dt.Compute("3 * (2+4)","");
Usando o compilador para fazer implica vazamentos de memória como as montagens geradas são carregados e nunca lançado. Também é menos eficaz do que usar um intérprete expressão real. Para este efeito, você pode usar Ncalc que é um framework open-source com este único propósito. Você também pode definir suas próprias variáveis ??e funções personalizadas se as já incluídas não são suficientes.
Exemplo:
Expression e = new Expression("2 + 3 * 5");
Debug.Assert(17 == e.Evaluate());
Tente isto:
static double Evaluate(string expression) {
var loDataTable = new DataTable();
var loDataColumn = new DataColumn("Eval", typeof (double), expression);
loDataTable.Columns.Add(loDataColumn);
loDataTable.Rows.Add(0);
return (double) (loDataTable.Rows[0]["Eval"]);
}
Você pode olhar para "XpathNavigator.Evaluate" Eu tenho usado este para processar expressões matemáticas para o meu GridView e ele funciona muito bem para mim.
Aqui está o código que usei para o meu programa:
public static double Evaluate(string expression)
{
return (double)new System.Xml.XPath.XPathDocument
(new StringReader("<r/>")).CreateNavigator().Evaluate
(string.Format("number({0})", new
System.Text.RegularExpressions.Regex(@"([\+\-\*])")
.Replace(expression, " ${1} ")
.Replace("/", " div ")
.Replace("%", " mod ")));
}
static double Evaluate(string expression) {
var loDataTable = new DataTable();
var loDataColumn = new DataColumn("Eval", typeof (double), expression);
loDataTable.Columns.Add(loDataColumn);
loDataTable.Rows.Add(0);
return (double) (loDataTable.Rows[0]["Eval"]);
}
Explicação de como funciona:
Primeiro, fazemos uma mesa no var loDataTable = new DataTable();
parte, assim como em um motor de base de dados (MS SQL, por exemplo).
Em seguida, uma coluna, com alguns parâmetros específicos (var loDataColumn = new DataColumn("Eval", typeof (double), expression);
).
O parâmetro "Eval"
é o nome da coluna (atributo ColumnName).
typeof (double)
é o tipo de dados a serem armazenados na coluna, que é igual a colocar System.Type.GetType("System.Double");
vez.
expression
é a string que o método Evaluate
recebe, e é armazenado no Expression
atributo da coluna. Este atributo é para um propósito muito específico (óbvio), o que é que cada linha que é colocado na coluna será fullfilled com a "Expressão", e aceita praticamente wathever pode ser colocado em uma consulta SQL. Consulte http: / /msdn.microsoft.com/en-us/library/system.data.datacolumn.expression(v=vs.100).aspx para saber o que pode ser colocado no atributo Expression, e como ele é avaliado.
Então, loDataTable.Columns.Add(loDataColumn);
adiciona o loDataColumn
coluna para a tabela de loDataTable
.
Em seguida, uma linha é adicionada à tabela com uma coluna personalizado com um atributo de Expressão, feito através loDataTable.Rows.Add(0);
. Quando adicionamos esta linha, a célula da coluna "Eval" do loDataTable
tabela é fullfilled automaticamente com o seu atributo "Expressão", e, se ele tiver operadores e consultas SQL, etc, ele é avaliado e então armazenado para a célula, de modo , aqui acontece a "mágica", a corda com operadores é avaliada e armazenados a uma célula ...
Finalmente, basta retornar o valor armazenado para a célula da coluna "Eval" na linha 0 (é um índice, começa a contar a partir de zero), e fazer uma conversão para um casal com return (double) (loDataTable.Rows[0]["Eval"]);
.
E isso é tudo ... trabalho feito!
E aqui um eaiser código de entender, que faz o mesmo ... Não é dentro de um método, e é explicada também.
DataTable MyTable = new DataTable();
DataColumn MyColumn = new DataColumn();
MyColumn.ColumnName = "MyColumn";
MyColumn.Expression = "5+5/5"
MyColumn.DataType = typeof(double);
MyTable.Columns.Add(MyColumn);
DataRow MyRow = MyTable.NewRow();
MyTable.Rows.Add(MyRow);
return (double)(MyTable.Rows[0]["MyColumn"]);
Em primeiro lugar, criar a tabela com DataTable MyTable = new DataTable();
Em seguida, uma coluna com DataColumn MyColumn = new DataColumn();
Em seguida, colocar um nome para a coluna. Isto para que possamos procurar em seu conteúdo quando ele é armazenado para a mesa. Feito via MyColumn.ColumnName = "MyColumn";
Então, o Expression, aqui podemos colocar uma variável do tipo string, neste caso há uma string pré-determinada "5 + 5/5", cujo resultado é 6.
O tipo de dados a ser armazenado para o MyColumn.DataType = typeof(double);
coluna
Adicionar a coluna à mesa ... MyTable.Columns.Add(MyColumn);
Faça uma fila para ser inserido para a mesa, que copia a estrutura da tabela DataRow MyRow = MyTable.NewRow();
Adicionar a linha para a mesa com MyTable.Rows.Add(MyRow);
E retornar o valor da célula na linha 0 do MyColumn
coluna da MyTable
mesa com return (double)(MyTable.Rows[0]["MyColumn"]);
Lição feito !!!
Este é um simples avaliador de expressão usando Pilhas
public class MathEvaluator
{
public static void Run()
{
Eval("(1+2)");
Eval("5*4/2");
Eval("((3+5)-6)");
}
public static void Eval(string input)
{
var ans = Evaluate(input);
Console.WriteLine(input + " = " + ans);
}
public static double Evaluate(String input)
{
String expr = "(" + input + ")";
Stack<String> ops = new Stack<String>();
Stack<Double> vals = new Stack<Double>();
for (int i = 0; i < expr.Length; i++)
{
String s = expr.Substring(i, 1);
if (s.Equals("(")){}
else if (s.Equals("+")) ops.Push(s);
else if (s.Equals("-")) ops.Push(s);
else if (s.Equals("*")) ops.Push(s);
else if (s.Equals("/")) ops.Push(s);
else if (s.Equals("sqrt")) ops.Push(s);
else if (s.Equals(")"))
{
int count = ops.Count;
while (count > 0)
{
String op = ops.Pop();
double v = vals.Pop();
if (op.Equals("+")) v = vals.Pop() + v;
else if (op.Equals("-")) v = vals.Pop() - v;
else if (op.Equals("*")) v = vals.Pop()*v;
else if (op.Equals("/")) v = vals.Pop()/v;
else if (op.Equals("sqrt")) v = Math.Sqrt(v);
vals.Push(v);
count--;
}
}
else vals.Push(Double.Parse(s));
}
return vals.Pop();
}
}
Esta é direito de execução esquerda, de modo necessidade de usar parestesia adequada para executar a expressão
// 2+(100/5)+10 = 32
//((2.5+10)/5)+2.5 = 5
// (2.5+10)/5+2.5 = 1.6666
public static double Evaluate(String expr)
{
Stack<String> stack = new Stack<String>();
string value = "";
for (int i = 0; i < expr.Length; i++)
{
String s = expr.Substring(i, 1);
char chr = s.ToCharArray()[0];
if (!char.IsDigit(chr) && chr != '.' && value != "")
{
stack.Push(value);
value = "";
}
if (s.Equals("(")) {
string innerExp = "";
i++; //Fetch Next Character
int bracketCount=0;
for (; i < expr.Length; i++)
{
s = expr.Substring(i, 1);
if (s.Equals("("))
bracketCount++;
if (s.Equals(")"))
if (bracketCount == 0)
break;
else
bracketCount--;
innerExp += s;
}
stack.Push(Evaluate(innerExp).ToString());
}
else if (s.Equals("+")) stack.Push(s);
else if (s.Equals("-")) stack.Push(s);
else if (s.Equals("*")) stack.Push(s);
else if (s.Equals("/")) stack.Push(s);
else if (s.Equals("sqrt")) stack.Push(s);
else if (s.Equals(")"))
{
}
else if (char.IsDigit(chr) || chr == '.')
{
value += s;
if (value.Split('.').Length > 2)
throw new Exception("Invalid decimal.");
if (i == (expr.Length - 1))
stack.Push(value);
}
else
throw new Exception("Invalid character.");
}
double result = 0;
while (stack.Count >= 3)
{
double right = Convert.ToDouble(stack.Pop());
string op = stack.Pop();
double left = Convert.ToDouble(stack.Pop());
if (op == "+") result = left + right;
else if (op == "+") result = left + right;
else if (op == "-") result = left - right;
else if (op == "*") result = left * right;
else if (op == "/") result = left / right;
stack.Push(result.ToString());
}
return Convert.ToDouble(stack.Pop());
}
Você poderia facilmente executar este através do CSharpCodeProvider com envolvimento fluff adequado, ele (um tipo e um método, basicamente). Da mesma forma que você poderia passar por VB etc - ou JavaScript, como outra resposta sugeriu. Eu não sei de qualquer outra coisa construída no quadro neste momento.
eu esperaria que o .NET 4.0 com o seu apoio para linguagens dinâmicas pode muito bem ter melhores capacidades nesta frente.
Recentemente eu precisava fazer isso para um projeto e acabei usando IronPython para fazê-lo. Você pode declarar uma instância do motor, e depois passar qualquer expressão python válidos e obter o resultado. Se você está apenas fazendo expressões matemáticas simples, então seria suficiente. Meu código acabou parecendo semelhante a:
IronPython.Hosting.PythonEngine pythonEngine = new IronPython.Hosting.PythonEngine();
string expression = "3*(2+4)";
double result = pythonEngine.EvaluateAs<double>(expression);
Você provavelmente não deseja criar o motor para cada expressão. Você também precisa de uma referência para IronPython.dll
EDIT:. percebi que realmente deve trazer a adição e subtração para fora separadamente aswell para torná-lo um pouco mais compatível BODMAS
Um grande obrigado ao Rajesh Jinaga para a sua abordagem baseada Stack. Eu achei muito útil para as minhas necessidades. O código seguinte é uma ligeira modificação do método de Rajesh, que processa a primeira divisões, multiplicações em seguida, em seguida, termina-se com a adição e subtracção. Ele também irá permitir o uso de booleans nas expressões, onde a verdadeira é tratado como 1 e falso 0. permitindo o uso de lógica booleana em expressões.
public static double Evaluate(string expr)
{
expr = expr.ToLower();
expr = expr.Replace(" ", "");
expr = expr.Replace("true", "1");
expr = expr.Replace("false", "0");
Stack<String> stack = new Stack<String>();
string value = "";
for (int i = 0; i < expr.Length; i++)
{
String s = expr.Substring(i, 1);
// pick up any doublelogical operators first.
if (i < expr.Length - 1)
{
String op = expr.Substring(i, 2);
if (op == "<=" || op == ">=" || op == "==")
{
stack.Push(value);
value = "";
stack.Push(op);
i++;
continue;
}
}
char chr = s.ToCharArray()[0];
if (!char.IsDigit(chr) && chr != '.' && value != "")
{
stack.Push(value);
value = "";
}
if (s.Equals("("))
{
string innerExp = "";
i++; //Fetch Next Character
int bracketCount = 0;
for (; i < expr.Length; i++)
{
s = expr.Substring(i, 1);
if (s.Equals("(")) bracketCount++;
if (s.Equals(")"))
{
if (bracketCount == 0) break;
bracketCount--;
}
innerExp += s;
}
stack.Push(Evaluate(innerExp).ToString());
}
else if (s.Equals("+") ||
s.Equals("-") ||
s.Equals("*") ||
s.Equals("/") ||
s.Equals("<") ||
s.Equals(">"))
{
stack.Push(s);
}
else if (char.IsDigit(chr) || chr == '.')
{
value += s;
if (value.Split('.').Length > 2)
throw new Exception("Invalid decimal.");
if (i == (expr.Length - 1))
stack.Push(value);
}
else
{
throw new Exception("Invalid character.");
}
}
double result = 0;
List<String> list = stack.ToList<String>();
for (int i = list.Count - 2; i >= 0; i--)
{
if (list[i] == "/")
{
list[i] = (Convert.ToDouble(list[i - 1]) / Convert.ToDouble(list[i + 1])).ToString();
list.RemoveAt(i + 1);
list.RemoveAt(i - 1);
i -= 2;
}
}
for (int i = list.Count - 2; i >= 0; i--)
{
if (list[i] == "*")
{
list[i] = (Convert.ToDouble(list[i - 1]) * Convert.ToDouble(list[i + 1])).ToString();
list.RemoveAt(i + 1);
list.RemoveAt(i - 1);
i -= 2;
}
}
for (int i = list.Count - 2; i >= 0; i--)
{
if (list[i] == "+")
{
list[i] = (Convert.ToDouble(list[i - 1]) + Convert.ToDouble(list[i + 1])).ToString();
list.RemoveAt(i + 1);
list.RemoveAt(i - 1);
i -= 2;
}
}
for (int i = list.Count - 2; i >= 0; i--)
{
if (list[i] == "-")
{
list[i] = (Convert.ToDouble(list[i - 1]) - Convert.ToDouble(list[i + 1])).ToString();
list.RemoveAt(i + 1);
list.RemoveAt(i - 1);
i -= 2;
}
}
stack.Clear();
for (int i = 0; i < list.Count; i++)
{
stack.Push(list[i]);
}
while (stack.Count >= 3)
{
double right = Convert.ToDouble(stack.Pop());
string op = stack.Pop();
double left = Convert.ToDouble(stack.Pop());
if (op == "<") result = (left < right) ? 1 : 0;
else if (op == ">") result = (left > right) ? 1 : 0;
else if (op == "<=") result = (left <= right) ? 1 : 0;
else if (op == ">=") result = (left >= right) ? 1 : 0;
else if (op == "==") result = (left == right) ? 1 : 0;
stack.Push(result.ToString());
}
return Convert.ToDouble(stack.Pop());
}
Eu sei que há probabilidade de ser uma maneira mais limpa de fazê-lo, pensei id apenas compartilhar a primeira olhada em alguém acha caso útil.
Muitas graças a Ramesh. Eu usei uma versão de seu código simples para puxar uma corda para fora um banco de dados e usá-lo para fazer operações de boolean no meu código.
x é um número como 1500 ou 2100 ou qualquer outra coisa.
função seria uma avaliação armazenada como x> 1400 e x <1600
function = relation[0].Replace("and","&&").Replace("x",x);
DataTable f_dt = new DataTable();
var f_var = f_dt.Compute(function,"");
if (bool.Parse(f_var.ToString()) { do stuff }
Não há. Você vai precisar usar alguma biblioteca externa, ou escrever seu próprio analisador. Se você tem tempo para fazê-lo, eu sugiro para escrever seu próprio analisador como é um projeto bastante interessante. Caso contrário, você vai precisar usar algo como bcParser .
A resposta curta: Eu não penso assim. C # .Net é compilado (para bytecode) e não pode avaliar cordas em tempo de execução, tanto quanto eu sei. JScript .NET podem, no entanto; mas eu ainda aconselho que você o código de um avaliador baseado em pilha analisador e de si mesmo.