我正在尝试评估一个以前缀表示法表示表达式的列表。以下是此类列表的示例:
[+, [sin, 3], [- 10 5]]
评估列表价值的最佳方法是什么
我正在尝试评估一个以前缀表示法表示表达式的列表。以下是此类列表的示例:
[+, [sin, 3], [- 10 5]]
评估列表价值的最佳方法是什么
如果您使用后缀而不是前缀,它会更简单。请参阅反向波兰表示法 (RPN)。给定 RPN 中的表达式,只需使用一个堆栈即可轻松评估它。
但是,由于您要求一种方法来评估前缀表达式而无需递归和使用堆栈(对于一种可能更简单的方法,请参阅下面的编辑:),这是一种方法:
我们可以使用两个堆栈来做到这一点。
一个堆栈(称为求值)保存运算符(如 +、sin 等)和操作数(如 3,4 等),另一个堆栈(称为计数)保存待查看的操作数数量 + 数量的元组运算符期望的操作数。
每当您看到一个运算符时,您就将运算符压入评估堆栈,并将相应的元组压入计数堆栈。
每当您看到一个操作数(如 3,5 等)时,您都会检查 Count 堆栈的顶部元组并减少该元组中剩余的操作数数量。
如果要查看的操作数数量变为零,则从 Count 堆栈中弹出元组。然后从评估堆栈中弹出所需的操作数数量(您知道这是因为元组的其他值),弹出运算符并执行操作以获得新值(或操作数)。
现在将新操作数推回评估堆栈。这个新的操作数推送使您再次查看 Count 堆栈的顶部,并且您执行我们刚刚所做的相同操作(减少看到的操作数,与零比较等)。
如果操作数计数不为零,则继续输入中的下一个标记。
例如,假设您必须评估 + 3 + 4 / 20 4
堆栈看起来像(左边是堆栈的顶部)
Count Evaluation Input
+ 3 + 4 / 20 4
(2,2) + 3 + 4 / 20 4
(2,1) 3 + + 4 / 20 4
(2,2) (2,1) + 3 + 4 / 20 4
(2,1) (2,1) 4 + 3 + / 20 4
(2,2) (2,1) (2,1) / 4 + 3 + 20 4
(2,1) (2,1) (2,1) 20 / 4 + 3 + 4
(2,0) (2,1) (2,1) 4 8 / 4 + 3 +
Since it has become zero, you pop off two operands, the operator /
and evaluate and push back 5. You pop off (2,0) from the Count stack.
(2,1) (2,1) 5 4 + 3 +
Pushing back you decrement the current Count stack top.
(2,0) (2,1) 5 4 + 3 +
Since it has become zero, you pop off 5,4 and + and evaluate and push back 9.
Also pop off (2,0) from the count stack.
(2,0) 9 3 +
12
编辑:
一位朋友建议了一种无需多个堆栈的方法:
从头开始,转到第一个操作员。右侧的标记将是操作数。评估并重做。似乎比使用两个堆栈简单得多。我们可以使用双向链表来表示我们在处理过程中更改的输入。评估时,删除节点,然后插入结果。或者你可以只使用一个堆栈。
KISS,反向评估为后缀表达式。
在我看来,你有两个选择。要么从左到右,要么从右到左(如上面保罗建议的那样)。这两种方法都在下面的代码中进行了演示。
public static class Evaluator
{
public static double EvaluatePrefix(string expr)
{
if (expr == null) throw new ArgumentNullException("expr");
var symbols = expr.Split(',');
var stack = new Stack<Symbol>();
foreach (var symbol in symbols)
{
double operand;
if (!double.TryParse(symbol, out operand)) //encountered an operator
{
stack.Push(new Operator(symbol));
continue;
}
//encountered an operand
if (stack.Count == 0) throw new ArgumentException("Invalid expression");
double right = operand;
var leftOperand = stack.Peek() as Operand;
while (leftOperand != null)
{
stack.Pop(); //pop left operand that we just peeked
if (stack.Count == 0) throw new ArgumentException("Invalid expression");
double result = Calculate(leftOperand.Value, right, ((Operator)stack.Pop()).OperatorChar);
right = result;
leftOperand = (stack.Count == 0) ? null : stack.Peek() as Operand;
}
stack.Push(new Operand(right));
}
if (stack.Count != 1) throw new ArgumentException("Invalid expression");
var operandSymbol = stack.Pop() as Operand;
if (operandSymbol == null) throw new ArgumentException("Invalid expression");
return operandSymbol.Value;
}
public static double EvaluatePrefixAlternative(string expr)
{
if (expr == null) throw new ArgumentNullException("expr");
double d;
var stack = new Stack<Symbol>(
expr.Split(',').Select(s => double.TryParse(s, out d) ? (Symbol) new Operand(d) : new Operator(s)));
var operands = new Stack<double>();
while (stack.Count > 0)
{
var symbol = stack.Pop();
var operand = symbol as Operand;
if (operand != null)
{
operands.Push(operand.Value);
}
else
{
if (operands.Count < 2) throw new ArgumentNullException("expr");
operands.Push(Calculate(operands.Pop(), operands.Pop(), ((Operator) symbol).OperatorChar));
}
}
if (operands.Count != 1) throw new ArgumentNullException("expr");
return operands.Pop();
}
private static double Calculate(double left, double right, char op)
{
switch (op)
{
case '*':
return (left * right);
case '+':
return (left + right);
case '-':
return (left - right);
case '/':
return (left / right); //May divide by zero !
default:
throw new ArgumentException(string.Format("Unrecognized operand {0}", op), "op");
}
}
abstract class Symbol
{
}
private class Operand : Symbol
{
public double Value { get; private set; }
public Operand(double value)
{
Value = value;
}
}
private class Operator : Symbol
{
public char OperatorChar { get; private set; }
public Operator(string symbol)
{
if (symbol.Trim().Length != 1) throw new ArgumentException("Invalid expression");
OperatorChar = symbol[0];
}
}
}
一些测试:
[TestMethod]
public void TestPrefixEvaluation()
{
Assert.AreEqual(5, Evaluator.EvaluatePrefix("-,*,/,15,-,7,+,1,1,3,+,2,+,1,1"));
Assert.AreEqual(4, Evaluator.EvaluatePrefix("/,-,*,2,5,*,1,2,-,11,9"));
Assert.AreEqual(5, Evaluator.EvaluatePrefixAlternative("-,*,/,15,-,7,+,1,1,3,+,2,+,1,1"));
Assert.AreEqual(4, Evaluator.EvaluatePrefixAlternative("/,-,*,2,5,*,1,2,-,11,9"));
}