您的位置:首页 > 其它

[原创]从中文智能提示到表达式识别与计算

2009-07-29 18:23 357 查看
前言

在此处我以表达式“3+(4*(1+(1+2)))*(1+2)(3+2)”,虽然谈到是中文提示到这篇的,但是为了画图和解释方便,暂且用数字直接代替。

事实上,中文表达式到具体的值只有一步而已,那就是识别=>反射=>读属性。

通过上述表达式可以看出,一个表达式中有两个关键元素,即:操作符(operator)和操作数(operand).

对于操作符而言,常见是分为一元和二元的,对于二元操作符自然是有2个操作数的,不同操作数之间类型的不同也导致了结果的输出的不同。

还要考虑操作符的优先级顺序,因为在表达式中,优先级决定了谁优先计算,这步的实现直接导致了表达式表达意义的对与错。

所以在对一段表达式做计算前,必须要完成三个步骤

1.合法性检测=>a.是否包含未完全匹配的小括号.异常反馈及处理。

b.是否出现无意义的参量或者表达式

c.非法操作符或非法操作数

2.表达式分析=>a.是否包含有中文表达式(如果有则要进行反射取值)

b.操作符的可操作类型,操作元个数

3.操作数的类型,类型的溢出=>字符串,数值等类型的比较。

STEP1:计算单元

既然本文是写表达式识别,那么就要先从表达式存储结构开始,由下图可以看出事例表达式即将被分离成若干计算单元:





由上图可以看出,最终此表达式被简化了,那么这些操作数和操作符,都以树的结构被构建出来,因此创建了一个FCNNodes的类来存储如此的计算小单元。

publicclassFCNNodes

[code]{
#region构造函数

publicFCNNodes(){}


publicFCNNodes(FCNOprandoprand,FCNNodesoprn1,FCNNodesoprn2,intndeep):this(){……}


publicFCNNodes(FCNOprandoprand,FCNNodesoprn1,FCNNodesoprn2):this(oprand,oprn1,oprn2,0){……}


publicFCNNodes(FCNOprandoprand,objectvalue):this(){……}

#endregion


#region属性定义

///<summary>

///运算符

///</summary>

publicFCNOprandOprand{get;set;}

///<summary>

///子计算节点1

///</summary>

publicFCNNodesOprn1{set;get;}

///<summary>

///子计算节点2

///</summary>

publicFCNNodesOprn2{get;set;}

privateobject_value=null;

///<summary>

///本计算结点内的计算结果

///</summary>

publicobjectValue

{

set{_value=value;}

get

{

if(_value!=null)

{

return_value;

}

else

{


/**

*根据节点Oprand去找出对应的运算方法。

*/

//计算式子

if((this.Oprand&FCNOprand.CALCULATER)!=0)

{

FCNCalculaterfcnc=newFCNCalculater();

_value=fcnc.Calculater(this);

}

//判断式子

if((this.Oprand&FCNOprand.JUDAGER)!=0)

{

FCNJudagerfcnj=newFCNJudager();

_value=fcnj.Judager(this);

}

return_value;

}

}

}

///<summary>

///计算式深度

///</summary>

publicintnDeep{set;get;}

#endregion

}

[/code]

STEP2:读取计算表达式

全匹配,从最内部开始,根据正则表达式,取一个表达式中最内部的表达式最后得到最简表达式。

最简表达式的计算符号识别,有很多的算法,此处只作为引子,会专门用一节去说明这个。

STEP3:如何计算

一个节点计算单元其目的就是为了算出Value值,所以外部只要访问这个Value自然就可以计算出来。在Value的内部,使用Oprand取找出对应的方法,Oprand是一个操作符枚举[Flags]。

到现在为止,分为两个计算类别:数值计算及逻辑计算。

数值计算的有:+-*/%

逻辑计算的有:==,>,<,!=,&&,||,>=,<=

这里只例举了一些常用的,当然你也可以自定义一些你认为比较有性格的计算符号-_-,它们的计算方式都被定义在FCNCalculater和FCNJudager。(其实当我写出来后,我发现这种组织方法并不是很好的,之后会有一些说明。)

现在用FCNJudager为例子:


publicclassFCNJudager

[code]{
publicFCNJudager()

{


}

///<summary>

///判断访问器

///</summary>

///<paramname="fcnn"></param>

///<returns></returns>

publicvirtualboolJudager(FCNNodesfcnn)

{

/**

*1.找出两者共有类型,如:oprn1是int型,oprn2是string型,那么返回string。

*/

boolresult=false;


switch(fcnn.Oprand)

{

caseFCNOprand.Equals:

{

#regionEqualsLogic

result=jageEqual(fcnn);

#endregion

}

break;

caseFCNOprand.Bigthan:

{

#regionBigthanLogic

result=BigSmallThan(fcnn.Oprn1.Value,fcnn.Oprn2.Value);

#endregion

}

break;

caseFCNOprand.Smallthan:

{

#regionSmallthanLogic

result=!BigSmallThan(fcnn.Oprn2.Value,fcnn.Oprn1.Value);

#endregion

}

break;

caseFCNOprand.NoEquals:

{

#regionNoEqualsLogic

result=!jageEqual(fcnn);

#endregion

}

break;

caseFCNOprand.AND:

{

#regionANDLogic

//True&&False

if(FCNTypeChecker.Checker<bool>(fcnn.Oprn1.Value)

&&FCNTypeChecker.Checker<bool>(fcnn.Oprn2.Value))

{

result=bool.Parse(fcnn.Oprn1.Value.ToString())&&bool.Parse(fcnn.Oprn2.Value.ToString());

}

#endregion

}

break;

caseFCNOprand.OR:

{

#regionORLogic

//True||False

if(FCNTypeChecker.Checker<bool>(fcnn.Oprn1.Value)

&&FCNTypeChecker.Checker<bool>(fcnn.Oprn2.Value))

{

result=bool.Parse(fcnn.Oprn1.Value.ToString())||bool.Parse(fcnn.Oprn2.Value.ToString());

}

#endregion

}

break;

caseFCNOprand.BigthanEquals:

{

#regionBigthanEqualsLogic

if(jageEqual(fcnn)||BigSmallThan(fcnn.Oprn1.Value,fcnn.Oprn2.Value))

{

result=true;

}

#endregion

}

break;

caseFCNOprand.SmallthanEquals:

{

#regionSmallthanEqualsLogic

if(jageEqual(fcnn)||!BigSmallThan(fcnn.Oprn2.Value,fcnn.Oprn1.Value))

{

result=true;

}

#endregion

}

break;

}

returnresult;

}

///<summary>

///大小比较

///</summary>

///<paramname="value1"></param>

///<paramname="value2"></param>

///<returns></returns>

privatestaticboolBigSmallThan(objectvalue1,objectvalue2)

{

boolbresult=false;

//int->datetime->string

if(FCNTypeChecker.Checker<int>(value1.ToString())

&&FCNTypeChecker.Checker<int>(value2.ToString()))

{

bresult=int.Parse(value1.ToString())>int.Parse(value2.ToString());

}

if(FCNTypeChecker.Checker<long>(value1.ToString())

&&FCNTypeChecker.Checker<long>(value2.ToString()))

{

bresult=long.Parse(value1.ToString())>long.Parse(value2.ToString());

}

if(FCNTypeChecker.Checker<DateTime>(value1.ToString())

&&FCNTypeChecker.Checker<DateTime>(value2.ToString()))

{

bresult=(DateTime.Parse(value1.ToString()).CompareTo(DateTime.Parse(value2.ToString()))==1);

}


if(value1.ToString().CompareTo(value2.ToString())==1)

{

bresult=true;

}

returnbresult;

}

///<summary>

///相等判断

///</summary>

///<paramname="fcnn"></param>

///<returns></returns>

privatebooljageEqual(FCNNodesfcnn)

{

boolbresult=false;

if(FCNTypeChecker.Checker<int>(fcnn.Oprn1.Value.ToString())

&&FCNTypeChecker.Checker<int>(fcnn.Oprn2.Value.ToString()))

{

bresult=int.Parse(fcnn.Oprn1.Value.ToString())==int.Parse(fcnn.Oprn2.Value.ToString());

}

if(FCNTypeChecker.Checker<long>(fcnn.Oprn1.Value.ToString())

&&FCNTypeChecker.Checker<long>(fcnn.Oprn2.Value.ToString()))

{

bresult=long.Parse(fcnn.Oprn1.Value.ToString())==long.Parse(fcnn.Oprn2.Value.ToString());

}

if(fcnn.Oprn1.Value.ToString().Equals(fcnn.Oprn2.Value.ToString()))

{

bresult=true;

}

returnbresult;

}

}

[/code]

在判断式中是以这样的步骤进行的:

1.首先根据节点单元中的操作符找到对应的操作逻辑。

2.在操作逻辑中,获取节点单元的两个子节点的Value。

3.判断返回的Value是何种类型。

外话

对于第三点特别说明一下,此处给的事例是很简单的,主要是先判断是否为int,然后在判断是否为long,最后才是string.(当然在这里考虑的方面依然是很幼稚的!)

如果是整型会有溢出,如果是一个是int,一个是string,那么就以string返回,等等这些都需要做Check,而如果在这个地方把所有的情况全部例举全,那是何等的壮观啊!!

此时,我想这些操作符的特性都限定在一个类里面,然后用一个操作符的集合去管理这些类。



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: