打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
一种字符串表达式求值的简单方法
在程序设计过程中,可能碰到需要对字符串型数学表达式进行求值,通用且完美的方法是将字符串表达解析,生成表达树,然后进行计算。编译器就是使用这种方法来解析程序中的表达式的。这种方法实现起来有点难度,需要考虑运算符的优先级,括号的配对,堆栈的使用等等。
我们正常情况下看到的数学表达式如果用二叉树遍历的话,恰好是中序遍历,故叫做中序表达式。除此之外,还有前序表达式,后序表达式。如:a+b+c(中序),++abc(前序),ab+c+(后序),如果表达式含有×,/,()等就更复杂了。
既然使用生成表达式树的方法直接执行通常的中序表达式有点困难,那就考虑一下其它的两个表达式吧。这里介绍如何用后序表达式来求值,这种方法早在以前就有人介绍了,因为其简单,易实现,故偶在这里重新阐述一下。
后缀表达式——将运算符放在两操作数的后面。后缀表达式也称逆波兰表达式 因其使表达式求值变得轻松,所以被普遍使用。
前缀和后缀表示法有三项公共特征:
1.操作数的顺序与等价的中缀表达式中操作数的顺序一致
2.不需要括号
3.操作符的优先级不相关
当然我们不可能自己特意去写一个后序表达式,这样很难受,因为我们早就习惯了中序表达式这种形式,因此我们需要将中序表达首先转化为后序表达式,去掉括号。中缀转后缀是栈应用的一个典型例子。其转换方法采用运算符优先法。转换过程需借助一个运算符栈和一个存放逆波兰表达式的数组。
转换方法如下:
1.将表达式开始符“#”压入运算符栈,作为栈底元素。
2.读入操作数,直接存入数组。
3.读入运算符,压入运算符栈。
若后进运算符优先级别高于当前栈顶元素时,则继续进栈;
若后进运算符优先级别低于或等于当前栈顶元素时,则将当前栈顶元素出栈,存入数组后进运算符入栈。
4.括号处理
遇到开括号"(",将括号进运算符栈;遇到闭括号")",则把最靠近的开括号,以及该开括号其遇到闭括号,后进栈的运算符依次出栈,存入数组(括号脱去)
5.遇到表达式结束符则把运算符栈内的所有运算符依次弹出,并存入数组。
后缀表达式的执行:
1.先读入两个操作数,遇到操作符,则将这两个操作数进行该操作符对应的计算。
2.保存计算结果,并将其作为下一个操作的第一个操作数。
3.读入下一个操作数,如果已到表达式结尾(即读到空),则算法结束,否则进行第4步
4.读入下一个操作符,进行对应的计算。转第2步。

上面的方法可以使用任意程序设计语言来实现。这里提供一个C#实现的类,该类利用了C#语言特有的一些性质,可以对任意的表达式求值,并且能够解析字符串形式的代码。类代码如下 :

using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using System.Text;
using System.Reflection;

namespace ADOGuy
{
/// <summary>
/// 此类用于C#计算字符串表达式语句,能计算的语句包括所有的数学表达式:
/// 简单加减乘除表达式:如1+2+2,1*5+1等,
/// 有数学函数的复杂表达:如1+Math.Cos(1),Math.Sqrt(20)等
/// 注意,方法的调用应与表达式的返回结果相对应,否则出错
/// </summary>

public class Evaluator
{

#region Construction
public Evaluator(EvaluatorItem[] items)
{
ConstructEvaluator(items);
}

public Evaluator(Type returnType, string expression, string name)
{
EvaluatorItem[] items
= { new EvaluatorItem(returnType, expression, name) };
ConstructEvaluator(items);
}


public Evaluator(string varDefineCode, Type returnType, string expression, string name)
...

public Evaluator(EvaluatorVarDefineItem subItem, Type returnType, string expression, string name)
{
EvaluatorItem[] items
= ...;
ConstructEvaluator(items);
}


public Evaluator(EvaluatorItem item)
{
EvaluatorItem[] items
= { item };
ConstructEvaluator(items);
}


private void ConstructEvaluator(EvaluatorItem[] items)
{
ICodeCompiler comp
= (new CSharpCodeProvider().CreateCompiler());//有警告,可以试试下一句
//CodeDomProvider comp = new CSharpCodeProvider();

CompilerParameters cp
= new CompilerParameters();
cp.ReferencedAssemblies.Add(
"system.dll");
cp.ReferencedAssemblies.Add(
"system.data.dll");
cp.ReferencedAssemblies.Add(
"system.xml.dll");
cp.GenerateExecutable
= false;
cp.GenerateInMemory
= true;

StringBuilder code
= new StringBuilder();//将要编译的代码串写入code
code.Append("using System; ");
code.Append(
"namespace ADOGuy { ");
code.Append(
" public class _Evaluator { ");
foreach (EvaluatorItem item in items)
{
code.AppendFormat(
" public {0} {1}() ",
item.ReturnType.Name,
item.Name);
code.Append(
"{ ");
code.Append(item.VarDefineCode
+ " ");
code.AppendFormat(
" return ({0}); ", item.Expression);
code.Append(
"} ");
}

code.Append(
"} }");

Console.WriteLine(code);

CompilerResults cr
= comp.CompileAssemblyFromSource(cp, code.ToString());//从code串构建程序
if (cr.Errors.HasErrors)
{
StringBuilder error
= new StringBuilder();
error.Append(
"Error Compiling Expression: ");
foreach (CompilerError err in cr.Errors)
{
error.AppendFormat(
"{0} ", err.ErrorText);
}

throw new Exception("Error Compiling Expression: " + error.ToString());
}

Assembly a
= cr.CompiledAssembly;
_Compiled
= a.CreateInstance("ADOGuy._Evaluator");//创建程序实例
}

#endregion


#region Public Members
public int EvaluateInt(string name)
{
return Convert.ToInt32(Evaluate(name));
}


public string EvaluateString(string name)
{
return Convert.ToString(Evaluate(name));
}


public bool EvaluateBool(string name)
{
return Convert.ToBoolean(Evaluate(name));
}


public float EvaluateFloat(string name)
{
return Convert.ToSingle(Evaluate(name));
}


public double EvaluateDouble(string name)
{
return Convert.ToDouble(Evaluate(name));
}


public object EvaluateObject(string name)
{
return Evaluate(name);
}


public object Evaluate(string name)
{
MethodInfo mi
= _Compiled.GetType().GetMethod(name);//利用实例调用由name指定的方法进行计算
return mi.Invoke(_Compiled, null);
}

#endregion


#region Static Members
static public int EvaluateToInteger(string code)
{
Evaluator eval
= new Evaluator(typeof(int), code, staticMethodName);
return Convert.ToInt32(eval.Evaluate(staticMethodName));
}


static public string EvaluateToString(string code)
{
Evaluator eval
= new Evaluator(typeof(string), code, staticMethodName);
return Convert.ToString(eval.Evaluate(staticMethodName));
}



static public bool EvaluateToBool(string code)
{
Evaluator eval
= new Evaluator(typeof(bool), code, staticMethodName);
return Convert.ToBoolean(eval.Evaluate(staticMethodName));
}


static public double EvaluateToDouble(string code)
{
Evaluator eval
= new Evaluator(typeof(double), code, staticMethodName);
return Convert.ToDouble(eval.Evaluate(staticMethodName));
}


static public object EvaluateToObject(string code)
{
Evaluator eval
= new Evaluator(typeof(object), code, staticMethodName);
return eval.Evaluate(staticMethodName);
}

static public int EvaluateToInteger(string varDefineCode, string code)
{

Evaluator eval
= new Evaluator(varDefineCode, typeof(int), code, staticMethodName);
return Convert.ToInt32(eval.Evaluate(staticMethodName));
}


static public string EvaluateToString(string varDefineCode, string code)
{
Evaluator eval
= new Evaluator(varDefineCode, typeof(string), code, staticMethodName);
return Convert.ToString(eval.Evaluate(staticMethodName));
}



static public bool EvaluateToBool(string varDefineCode, string code)
{
Evaluator eval
= new Evaluator(varDefineCode, typeof(bool), code, staticMethodName);
return Convert.ToBoolean(eval.Evaluate(staticMethodName));
}


static public double EvaluateToDouble(string varDefineCode, string code)
{
Evaluator eval
= new Evaluator(varDefineCode, typeof(double), code, staticMethodName);
return Convert.ToDouble(eval.Evaluate(staticMethodName));
}


static public object EvaluateToObject(string varDefineCode, string code)
{
Evaluator eval
= new Evaluator(varDefineCode, typeof(object), code, staticMethodName);
return eval.Evaluate(staticMethodName);
}


#endregion


#region Private
const string staticMethodName = "__foo";
Type _CompiledType
= null;
object _Compiled = null;
#endregion

}


#region
public class EvaluatorItem
{
public EvaluatorItem(Type returnType, string expression, string name)
{
ReturnType
= returnType;
Expression
= expression;
Name
= name;
}


public EvaluatorItem(string varDefineCode, Type returnType, string expression, string name)
{
VarDefineCode
= varDefineCode;
ReturnType
= returnType;
Expression
= expression;
Name
= name;
}


public EvaluatorItem(EvaluatorVarDefineItem subItem, Type returnType, string expression, string name)
{
VarDefineCode
= subItem.getVarDefineCode();
ReturnType
= returnType;
Expression
= expression;
Name
= name;
}


public string VarDefineCode = "";
public Type ReturnType;
public string Name;
public string Expression;
}

#endregion


#region
public class EvaluatorVarDefineItem
{
private string VarDefineCode = "";

public EvaluatorVarDefineItem(){}

public EvaluatorVarDefineItem(Type varType, string varName, string varValue)
{
this.VarDefineCode = varType.Name + " " + varName.Trim() + " = " + varValue.Trim() + ";";
}


public EvaluatorVarDefineItem(Type varType, string varExpression)
{
this.VarDefineCode = varType.Name + " " + varExpression + ";";
}


public string getVarDefineCode()
{
return this.VarDefineCode;
}


public void addVarExpression(Type varType, string varName, string varValue)
{
this.VarDefineCode += " "+varType.Name + " " + varName.Trim() + " = " + varValue.Trim() + ";";
}


public void addVarExpression(Type varType, string varExpression)
{
this.VarDefineCode += " " + varType.Name + " " + varExpression + ";";

}

}
;
#endregion


}

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
前缀、中缀、后缀表达式
2、Java 基础语法
day05_运算符入门
C# 将字符串解析运算并返回结果
JAVA中&&和&、||和|(短路与和逻辑与、短路或和逻辑或)的区别
C语言陷阱和缺陷
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服