如题.

解决方案 »

  1.   

    My_first(小@_@小) 
    如何做呢!!?
      

  2.   

    要做一个公式解析器:一款设计精巧的表达式解析器 
    ---- 开发MIS系统时,报表设计中经常会碰到表达式解释器,完成用户自定义的公式运算。这种程序的设计需要有比较高的技巧,以下介绍一款用DELPHI4.0开发的程序[程序重在算法,语言特性很少,用其它语言的人也能读懂],只要按自已的要求稍加修改,即可做成组件或全局方法发部。它支持 "加[+]、减[-]、乘[*]、除[/]、商[$:两整数相除,结果的整数部分]、模[%]、括号[()]"四则混合运算,支持"与[&]、或[|]、异或[^]、左移[< ]、右移[ >]和非[!]"逻辑运算功能,同时它们可以出现在同一个表达式中,它们的优先级依次为括号、非、与或异或左右移、乘除商模、加减。如式:12.45+3*16 >2*(3+6*(3+2)-1)=12.45+3*4*32,计算结果为:396.45。程序包括两大部分功能:表达式拆解、因子计算,分别由两个类TBdsProc和TPUSHPOP完成。具体如下: CDIKind=record
      case id: Boolean of
       True: (dval: Double);
       False: (ival: Integer);
     end;
    CDKind:区别表达式中的整数和浮点数类型,
    因为有些运算符不支持浮点数(如逻辑运算)。ValKind = CDIKind;
    TBdsProc = class
     private
      Fghpd : Integer;//识别并标记左右括号是否成对出现
      function IsCalcFh(c: Char): boolean;
      //判别一个字符是否运算符
      function CopyRight(abds: String;start: Integer):
      String;//截取字符串表达式
      function BdsSs(var abds: String): ValKind;
      //返回一个子表达式的值
      function BdsYz(var abds: String): ValKind;
      //表达式因子,如:15、(13+5)
      function BdsItm(var abds: String): ValKind;
      //读取表达式中的一个因子
     public
      function CalcValue(const bds: String): ValKind;
      //返回计算结果
     end;TPUSHPOP = class
     private
      ffh: array [0..2] of Char;//符号数组
      value: array [0..3] of CDIKind;//值数组
      flevel: Byte;//因子个数
      fisfh: Boolean;//识别等待输入值或运算符
      fisnot: Boolean;//识别待计算数据项是否执行非运算
      function Calcsj(av1,av2: CDIKind;fh: Char): CDIKind;
      //执行两个数值的四则运算
      function Calclg(av1,av2: CDIKind; fh: Char): CDIKind;
      //执行两个数的逻辑运算
      procedure Calccur;{当输入数据项满足四个数后
    [依运算优先级层数求得,见下述算式解析原理]执行中间运算}
      function IsLgFh(fh: Char): Boolean;
      //一个符号是否逻辑运算符
      function IsCcFH(fh: Char): Boolean;
      // 一个符号乘除商模运算符
     public
      constructor Create;
      procedure PushValue(avalue: CDIKind);//存入一个数据项
      procedure PushFh(afh: Char);//存入一个符号
      function CalcValue: CDIKind;//计算并返回值
     end;---- 表达式解析基本原理: ---- 1.表达式处理: ---- 表达式的一个个数据项组成,中间由运算符连接,每个数据项为一个分析基本分析单元。表达式中如果包含有改变运算优先级别的括号运算,先计出括号中式子的值,再把该值当一个数据项处理,这一点在程序设计中只要运用递归功就能实现。 ---- 2.数据项计算处理 ---- a >非运算: ---- 它为单目运算符,级别最高,在存入符号时做标记,存入数据时即时计算并去除标记。 ---- b >表达式运算: ---- 设f1、f2、f3分别表示一二三级运算符,V1、V2、V3、V4分别表示顺序四个数,则极端表达式模型为R=V1 f1 V2 f2 V3 f3 V4 …,计算时顺序应为 R=…V4 f3 V3 f2 V2 f1 V1。为了简化运算,把其中运算级别最高的逻辑运算在存入数据时先计算完成, 初始化时设V1=0,第一个运算符设为'+'。则公式化为: R=…V4 f2(f1) V3 f2(f1) V2 f1 V1。这样,当V2与V3间的运算符级别为f2时,V4与V3间的运算符级别< =f2,则:V2 =(V2与V3计算值),V3后的值和运算符前移;若V2与V3间的运算级别为f1,可先算V1与V2,V2以后的值和运算符前移。则计算后的表达式为:R=V3 f2(f2) V2 f1 V1刚好满足循环取数条件。 ---- 3.实现: ---- 程序比较长(TBdsProc和TPUSHPOP的源代码合计长度为400多行),完整代码见附件,以下对一些重要实现方法做介绍: ---- < 1 >表达式拆解:由方法BdsSs和BdsYz完成表达式拆解和因子处理 function TBdsProc.BdsSs(var abds: String): ValKind;
    var
     c: Char;
     lpp: TPushPop;
    begin
     lpp := TPushPop.Create;//建立数据计算对象
     while abds<  >'' do
     begin
      c := abds[1];
      if IsCalcFh(c) then//是否运算符
      begin
       lpp.PushFh(c);//保存运算符
       abds := CopyRight(abds,2);
      end
      else
      begin
       if c=')' then
       begin
        Dec(Fghpd);//括号匹配
        abds := CopyRight(abds,2);
        if Fghpd <  0 then
         Raise Exception.Create('括号不配对');
        Result := lpp.CalcValue;
        //返回括号中的子项值,进行下一步计算
        lpp.Free;
        Exit;
       end
       else
       begin
        if c='(' then
         Inc(Fghpd);//做括号层数标识
        lpp.PushValue(BdsYz(abds));//取下一项的值。
       end;
      end;
     end;
     if Fghpd<  >0 then
      Raise Exception.Create('括号不配对');
     Result := lpp.CalcValue;//返回最终运算值
     lpp.Free;
    end;function TBdsProc.BdsYZ(var abds: String): ValKind;
    begin
     if abds<  >'' then
     begin
      if abds[1]='(' then
      begin
       abds := CopyRight(abds,2);
       Result := BdsSs(abds);//递归调用,求括号中的值
      end
      else
       Result := BdsItm(abds);{读一个数据项,
    如果包括函数定义,可以在该方法中定义接口或连接}
     end;
    end;
    若表达式要支持函数功能,只要定义系统支持的函数,
    在取行表达式因子BdsItm中加入连接函数定义即可,如下:
    …..
    else if (c< ='Z') and (c >='A') then
    begin
      bhs := 取出函数名及参数
      Result := … //调用函数处理 (函数定义)
      abds := 下一项
    end
    ….
    < 2 > 数据计算:主要包PushValue,PushFh,
    Calccur分别完成存入数、符号、中间计算
    procedure TPUSHPOP.PushValue(avalue: CDIKind);
    begin
     if fisfh=True then
      Raise Exception.Create('缺少运算符');
     if fisnot then//进行非运算
     begin
      if avalue.id then
       Raise Exception.Create('浮点数不能做非运算');
      avalue.ival := not avalue.ival;
      fisnot := False;
     end;
     if IsLgFh(ffh[flevel]) then//运行逻辑运算
     begin
      value[flevel] := Calclg(value[flevel],
    avalue,ffh[flevel]);
    //与当前值做逻辑运算
     end
     else
     begin
      Inc(flevel);//存数位置指针加1
      value[flevel] := avalue;//存入值
      if flevel >2 then//数据个数达到4,进行中间运算
       Calccur;
     end;
     fisfh := True;//输入符号可见
    end;procedure TPUSHPOP.PushFh(afh: Char);
    begin
     if (fisfh=false) then//非运算是一级
     begin
      if (afh='!') and (not fisnot) then//标识非运算
      begin
       fisnot := True;
       Exit;
      end
      else
       Raise Exception.Create('运算符重复');
     End
     Else
     begin
      ffh[flevel] := afh;//存入运算符
      fisfh := False; 输入值可见
     end;
    end;procedure TPUSHPOP.Calccur;
    begin
     if IsCcFh(ffh[1]) then//二级运算符
     begin
      value[1] := Calcsj(value[1],value[2],ffh[1]);
      //计算2和3项的值
      ffh[1] := ffh[2];//后序运符和值前移
      value[2] := value[3];
     end
     else//一级运算符
     begin
      value[0] := Calcsj(value[0],value[1],ffh[0]);
      //计算1和2项的值
      value[1] := value[2];{ 后序运符和值前移, 
    2和3项的值不计算是为了保持第一个运算符为一级运算符}
      value[2] := value[3];
      ffh[0] := ffh[1];
      ffh[1] := ffh[2];
     end;
     Dec(flevel);//存数位置指针减1
    end;
      

  3.   

    用一个专门的数据库表来保存公式
    例如表结构为
    Type      Name      Value
    -----------------------------
    formula   工资      (3*:A+2*:B)/30+500
    param     工资       A
    param     工资       B然后你自己用写一个程序去读取解析公式就可以了
    公式变动时,只需要改变该表的内容即可
      

  4.   

    解析 表达式的最好办法 是用 SQL 语句:
    SELECT 表达式 AS ABCD FROM 表名(表中要有一条记录)
    ABCD 中就是 表达式的值,如果有自定义函数,要用存储过程定义该定义函数
      

  5.   

    就是happy243(HH)说的这个意思。自己写个公式解析. 一般工资的算法很简单的,就是加 、减、乘、除、( ) = 也就够用了。连接,分析这些字符,取出保存在数据库里。用的时候连接成sql语句。
      

  6.   

    cg1120(代码最优化-§新年祝福你,好运伴着你§)
    非常感谢你。可以把你写好的程序的部分代码发给我吗?
    [email protected]我还想问大家的是,想用宏替换能否做得到,delphi 的宏是怎么回事!?
      

  7.   

    to : banana12345(华北电力大学) 
    还是不大懂你的意思。to : My_first(小@_@小) ( ) 
    小弟愚钝,请详细告之:就那个简单的公式,把那些符号取出保存在数据库里。用的时候怎么连接成sql语句。
      

  8.   

    下官有一法,楼主可以一试
    原理,直接用字段名作为公式因子
    好处,减小程序复杂性,用户操作方便明了
    缺点,需事先将所有工资项目建表假如工资表有字段如下工号  基本1 水电2 出勤3 业绩4 其它5
    EmpNo MJB  MSD   MCC   MYJ   MQT现在,计算工资,有如下几种方案工资发放方案A = 基本工资
    工资发放方案B = 基本工资+出勤
    工资发放方案C = 基本工资+出勤+业绩*0.3
    工资发放方案D = 基本工资+出勤+业绩
    工资发放方案E = 全部项之和建一表,将如下关系一一对应,这是工资项目
    基本 水电 出勤 业绩 其它
    MJB  MSD   MCC   MYJ   MQT用户自定义工式时,将中文(基本 水电 出勤 业绩 其它)展示给用户,提供一些操作符
    现有一法,仅供参考
    选择一个字段时,将相对应的编号取出,放入一隐藏文本框,而在显示文本框则显示中文单击基本,则显示框为“基本”,隐藏框为“MJB”
    单击加号,则显示框为“基本+”,隐藏框为“MJB+”
    单击出勤,则显示框为“基本+出勤”,隐藏框为“MJB+MCC”
    单击加号,则显示框为“基本+出勤+”,隐藏框为“MJB+MCC+”
    单击业绩,则显示框为“基本+出勤+业绩”,隐藏框为“MJB+MCC+MYJ”
    单击乘号,则显示框为“基本+出勤+业绩*”,隐藏框为“MJB+MCC+MYJ*”
    单击常数,输入0.3,则显示框为“基本+出勤+业绩*0.3”,隐藏框为“MJB+MCC+MYJ*0.3”因为隐藏框中的项都是数据库的字段,所以,完全可以放入到SQL语句中执行。
    好了,将SQL语句存起来,改名为“工资发放方案C”其它运算符号同理,可以采用所有SQL支持的符号
    多少给我点分
      

  9.   

    谢谢大家参与问题已经解决,铁别感谢:
    cg1120(代码最优化-§新年祝福你,好运伴着你§)banana12345(华北电力大学) My_first(小@_@小) ( )