关闭

表达式计算器

221人阅读 评论(0) 收藏 举报
分类:

表达式计算器, 支持四则混合运算, 常量代入

扩展后可实现表达式嵌套引用, 防循环调用等功能


//公式解析单元

[delphi] view plaincopy
  1. unit utFormula;  
  2.   
  3. interface  
  4.   uses SysUtils, Generics.Collections;  
  5. type  
  6.   //表达式计算器  
  7.   TFormula = class  
  8.   private const  
  9.     OPE = ['+''-''*''/''('')']; //允许出现的运算符  
  10.     OPEFORCALC = ['+''-''*''/'];    //参与最终计算的运算符  
  11.   
  12.   private  
  13.     ConstValue : TDictionary<string, Double>;  //待替换的常量表  
  14.   private  
  15.     // 判断是否是操作数  
  16.     function IsOperand(ch: Char): boolean;  
  17.     // 返回运算符的优先级  
  18.     function Priority(ch: Char): integer;  
  19.     // 对两个值利用运算符计算结果  
  20.     function GetValue(op: Char; const val1, val2: Double): Double;  
  21.     // 判断是否数字  
  22.     function isNumber(const s: string): boolean;  
  23.     // 根据原生公式串生成公式分项列表(后缀表达式), 公式操作数列表  
  24.     procedure splitExp(FExp : TList<string>; const sValue: string);  
  25.     //依据后缀表达式计算结果  
  26.     function CalcItem(FExp : TList<string>): Double;  
  27.     //将字符串转为有效双精度值  
  28.     function CustomStrToDouble(const Key: string): Double;  
  29.   public  
  30.     constructor Create;  
  31.     destructor Destroy; override;  
  32.     //计算表达式  
  33.     function Calc(const AFormulaStr : string) : Double;  
  34.   end;  
  35. implementation  
  36.   
  37. { TFormula }  
  38. function TFormula.CustomStrToDouble(const Key : string): Double;  
  39. begin  
  40.   Result := 0;  
  41.   
  42.   if isNumber(Key) then   //如果是数值, 直接返回  
  43.     Result := StrToFloat(Key)  
  44.   else  
  45.   if not ConstValue.TryGetValue(Key, Result) then  //如果是常量则返回, 否则报出异常  
  46.     raise Exception.Create('无法识别的数值!');  
  47. end;  
  48.   
  49. function TFormula.CalcItem(FExp : TList<string>): Double;  
  50. var  
  51.   i: integer;  
  52.   valstring;  
  53.   stack: TStack<Double>; //堆栈;  
  54.   d1, d2: Double;  
  55. begin  
  56.   Result := 0;  
  57.   stack := TStack<Double>.create;  //创建栈对象  
  58.   try  
  59.     if FExp.Count = 1 then  //只有一个数据项时, 直接转换结果  
  60.     begin  
  61.       Result := CustomStrToDouble(FExp.Items[0]);  
  62.     end  
  63.     else  
  64.     begin  
  65.       for i := 0 to FExp.Count - 1 do   //逐项计算  
  66.       begin  
  67.         val := FExp.Items[i];  
  68.         if (Length(val) = 1and (val[1in OPEFORCALC) then  //是运算符则计算, 反之压栈  
  69.         begin  
  70.           d2 := stack.Pop;  
  71.           d1 := stack.Pop;  
  72.           Result := GetValue(val[1], d1, d2); //根据运算符和数据项计算结果  
  73.           stack.push(Result);   //计算结果压栈  
  74.         end  
  75.         else  
  76.           stack.push(CustomStrToDouble(val));  
  77.       end;  
  78.     end;  
  79.   finally  
  80.     stack.Free;  
  81.   end;  
  82. end;  
  83. function TFormula.Calc(const AFormulaStr: string): Double;  
  84. var  
  85.   FExp : TList<string>; // 公式分项列表, 后缀表达式  用于表达式计算  
  86. begin  
  87.   result := 0;  
  88.   if sametext(AFormulaStr, ''then  
  89.     raise Exception.Create('无效的表达式');  
  90.   
  91.   FExp := TList<string>.Create;  
  92.   try  
  93.     try  
  94.       splitExp(FExp , AFormulaStr); //产生后缀表达式  
  95.       result := CalcItem(FExp);  //计算后缀表达式  
  96.   
  97.     except on E: Exception do  
  98.       raise Exception.Create(e.Message);  
  99.     end;  
  100.   finally  
  101.     FExp.Free;  
  102.   end;  
  103. end;  
  104.   
  105. constructor TFormula.Create;  
  106. begin  
  107.   //创建常量表, 并初始化  
  108.   ConstValue := TDictionary<string, Double>.Create;  
  109.   ConstValue.Add('∏'3.1415926);  
  110. end;  
  111.   
  112. destructor TFormula.Destroy;  
  113. begin  
  114.   ConstValue.Free;  //销毁常量表  
  115.   inherited;  
  116. end;  
  117.   
  118. function TFormula.GetValue(op: Char; const val1, val2: Double): Double;  
  119. begin  
  120.   case op of  
  121.     '+':  
  122.       Result := val1 + val2;  
  123.     '-':  
  124.       Result := val1 - val2;  
  125.     '*':  
  126.       Result := val1 * val2;  
  127.     '/':  
  128.       begin  
  129.         if val2 = 0 then  
  130.           Result := 0  
  131.         else  
  132.           Result := val1 / val2;  
  133.       end;  
  134.   end;  
  135. end;  
  136.   
  137. function TFormula.isNumber(const s: string): boolean;  
  138. var  
  139.   i: Extended;  
  140.   Code: integer;  
  141. begin  
  142.   val(s, i, Code);  
  143.   Result := Code = 0;  
  144. end;  
  145.   
  146. function TFormula.IsOperand(ch: Char): boolean;  
  147. begin  
  148.   Result := not(ch in OPE);  
  149. end;  
  150.   
  151. function TFormula.Priority(ch: Char): integer;  
  152. begin  
  153.   case ch of  
  154.     '+':  
  155.       Result := 1;  
  156.     '-':  
  157.       Result := 1;  
  158.     '*':  
  159.       Result := 2;  
  160.     '/':  
  161.       Result := 2;  
  162.   else  
  163.     Result := 0;  
  164.   end;  
  165. end;  
  166.   
  167. procedure TFormula.splitExp(FExp : TList<string>; const sValue: string);  
  168.   function AddToExp(const Value : string): string;  
  169.   begin  
  170.     Result := '';  
  171.     if Value = '' then Exit;  
  172.   
  173.     FExp.Add(Value);  
  174.   end;  
  175. var  
  176.   ch, ch1: Char;  
  177.   stack: TStack<char>; // 堆栈;  
  178.   i: integer;  
  179.   sOperand: string;  
  180.   
  181. begin  
  182.   // 利用集合模拟堆栈操作,产生后缀表达式  
  183.   
  184.   stack := TStack<char>.create;  
  185.   try  
  186.     sOperand := '';  
  187.     for i := 1 to Length(sValue) do  
  188.     begin  
  189.       if sValue[i] = chr(32then  
  190.         Continue; // 去除多余的空字符  
  191.   
  192.       ch := sValue[i];  
  193.       if IsOperand(ch) then // 如果是操作数,直接放入B中  
  194.       begin  
  195.         sOperand := sOperand + ch;  
  196.       end  
  197.       else  
  198.       begin  
  199.         sOperand := AddToExp(sOperand);  
  200.         if ch = '(' then // 如果是'(',将它放入堆栈中  
  201.         begin  
  202.           stack.Push(ch);  
  203.         end  
  204.         else if ch = ')' then // 如果是')'  
  205.         begin  
  206.           while stack.Count > 0 do // 不停地弹出堆栈中的内容,直到遇到'('  
  207.           begin  
  208.             ch := stack.Pop;  
  209.             if ch = '(' then  
  210.               break  
  211.             else  
  212.             begin  
  213.               FExp.Add(ch);  
  214.             end;  
  215.           end;  
  216.         end  
  217.         else // 既不是'(',也不是')',是其它操作符,比如+, -, *, /之类的  
  218.         begin  
  219.           if stack.Count > 0 then  
  220.           begin  
  221.             while stack.Count > 0 do  
  222.             begin  
  223.               ch1 := stack.Pop; // 弹出栈顶元素  
  224.               if Priority(ch) > Priority(ch1) then // 如果栈顶元素的优先级小于读取到的操作符  
  225.               begin  
  226.                 stack.push(ch1); // 将栈顶元素放回堆栈  
  227.                 stack.push(ch); // 将读取到的操作符放回堆栈  
  228.                 break;  
  229.               end  
  230.               else // 如果栈顶元素的优先级比较高或者两者相等时  
  231.               begin  
  232.                 FExp.Add(ch1);  
  233.                 if stack.Count = 0 then  
  234.                 begin  
  235.                   stack.push(ch); // 将读取到的操作符压入堆栈中  
  236.                   break;  
  237.                 end;  
  238.               end;  
  239.             end;  
  240.           end  
  241.           else  
  242.             stack.push(ch); // 如果堆栈为空,就把操作符放入堆栈中  
  243.         end;  
  244.       end;  
  245.     end;  
  246.     AddToExp(sOperand);  
  247.   
  248.     while stack.Count > 0 do  
  249.       AddToExp(stack.Pop);  
  250.   
  251.   finally  
  252.     stack.Free;  
  253.   end;  
  254. end;  
  255. end.  

//调用单元
[delphi] view plaincopy
  1. unit utForm;  
  2.   
  3. interface  
  4.   
  5. uses  
  6.   SysUtils, Classes, Controls, Forms, Dialogs, StdCtrls, ComCtrls, Buttons, ToolWin;  
  7.   
  8. type  
  9.   TFrmMain = class(TForm)  
  10.     grp1: TGroupBox;  
  11.     grp2: TGroupBox;  
  12.     mmoFormula: TMemo;  
  13.     mmoValue: TMemo;  
  14.     tlb1: TToolBar;  
  15.     btnCalc: TSpeedButton;  
  16.     procedure btnCalcClick(Sender: TObject);  
  17.   private  
  18.     { Private declarations }  
  19.   public  
  20.     { Public declarations }  
  21.   end;  
  22. var  
  23.   FrmMain: TFrmMain;  
  24.   
  25. implementation  
  26. uses utFormula;  
  27.   
  28. {$R *.dfm}  
  29. procedure TFrmMain.btnCalcClick(Sender: TObject);  
  30. var  
  31.   AFormula : TFormula;  
  32.   i : integer;  
  33.   Value : Double;  
  34. begin  
  35.   mmoValue.Clear;  
  36.   AFormula := TFormula.Create;  
  37.   try  
  38.     for i := 0 to mmoFormula.Lines.Count - 1 do  
  39.     begin  
  40.       try  
  41.         //表达式计算  
  42.         Value := AFormula.Calc(mmoFormula.Lines.Strings[i]);  
  43.         //打印结果  
  44.         mmoValue.Lines.Add(mmoFormula.Lines.Strings[i] + '=' + FloatToStr(Value));  
  45.       except  
  46.         on E: Exception do  //异常处理  
  47.         begin  
  48.           ShowMessage(e.Message);  
  49.           mmoValue.Lines.Add(mmoFormula.Lines.Strings[i] + '=?????????');  
  50.         end;  
  51.       end;  
  52.     end;  
  53.   finally  
  54.     AFormula.Free;  
  55.   end;  
  56. end;  
  57.   
  58.   
  59.   
  60. end.  

//调用单元窗体代码
[delphi] view plaincopy
  1. object FrmMain: TFrmMain  
  2.   Left = 0  
  3.   Top = 0  
  4.   Caption = #34920#36798#24335#35745#31639  
  5.   ClientHeight = 479  
  6.   ClientWidth = 735  
  7.   Color = clBtnFace  
  8.   Font.Charset = DEFAULT_CHARSET  
  9.   Font.Color = clWindowText  
  10.   Font.Height = -11  
  11.   Font.Name = 'Tahoma'  
  12.   Font.Style = []  
  13.   OldCreateOrder = False  
  14.   PixelsPerInch = 96  
  15.   TextHeight = 13  
  16.   object grp1: TGroupBox  
  17.     Left = 0  
  18.     Top = 29  
  19.     Width = 392  
  20.     Height = 450  
  21.     Align = alLeft  
  22.     Caption = #34920#36798#24335  
  23.     TabOrder = 0  
  24.     object mmoFormula: TMemo  
  25.       Left = 2  
  26.       Top = 15  
  27.       Width = 388  
  28.       Height = 433  
  29.       Align = alClient  
  30.       Lines.Strings = (  
  31.         '1+2'  
  32.         '2*2'  
  33.         '(1+2*3-3)/2'  
  34.         #8719'+1')  
  35.       TabOrder = 0  
  36.       ExplicitLeft = 45  
  37.       ExplicitWidth = 341  
  38.     end  
  39.   end  
  40.   object grp2: TGroupBox  
  41.     Left = 392  
  42.     Top = 29  
  43.     Width = 343  
  44.     Height = 450  
  45.     Align = alClient  
  46.     Caption = #35745#31639#32467#26524  
  47.     TabOrder = 1  
  48.     ExplicitTop = 0  
  49.     ExplicitWidth = 249  
  50.     ExplicitHeight = 479  
  51.     object mmoValue: TMemo  
  52.       Left = 2  
  53.       Top = 15  
  54.       Width = 339  
  55.       Height = 433  
  56.       Align = alClient  
  57.       TabOrder = 0  
  58.       ExplicitTop = 72  
  59.       ExplicitWidth = 181  
  60.       ExplicitHeight = 405  
  61.     end  
  62.   end  
  63.   object tlb1: TToolBar  
  64.     Left = 0  
  65.     Top = 0  
  66.     Width = 735  
  67.     Height = 29  
  68.     ButtonHeight = 26  
  69.     ButtonWidth = 121  
  70.     Caption = 'tlb1'  
  71.     TabOrder = 2  
  72.     ExplicitLeft = 376  
  73.     ExplicitWidth = 150  
  74.     object btnCalc: TSpeedButton  
  75.       Left = 0  
  76.       Top = 0  
  77.       Width = 128  
  78.       Height = 26  
  79.       BiDiMode = bdLeftToRight  
  80.       Caption = #24320#22987#35745#31639  
  81.       ParentBiDiMode = False  
  82.       OnClick = btnCalcClick  
  83.     end  
  84.   end  
  85. end  
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:37258次
    • 积分:575
    • 等级:
    • 排名:千里之外
    • 原创:9篇
    • 转载:86篇
    • 译文:0篇
    • 评论:3条
    文章分类