表达式计算器

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

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


//公式解析单元

unit utFormula;

interface
  uses SysUtils, Generics.Collections;
type
  //表达式计算器
  TFormula = class
  private const
    OPE = ['+', '-', '*', '/', '(', ')']; //允许出现的运算符
    OPEFORCALC = ['+', '-', '*', '/'];    //参与最终计算的运算符

  private
    ConstValue : TDictionary<string, Double>;  //待替换的常量表
  private
    // 判断是否是操作数
    function IsOperand(ch: Char): boolean;
    // 返回运算符的优先级
    function Priority(ch: Char): integer;
    // 对两个值利用运算符计算结果
    function GetValue(op: Char; const val1, val2: Double): Double;
    // 判断是否数字
    function isNumber(const s: string): boolean;
    // 根据原生公式串生成公式分项列表(后缀表达式), 公式操作数列表
    procedure splitExp(FExp : TList<string>; const sValue: string);
    //依据后缀表达式计算结果
    function CalcItem(FExp : TList<string>): Double;
    //将字符串转为有效双精度值
    function CustomStrToDouble(const Key: string): Double;
  public
    constructor Create;
    destructor Destroy; override;
    //计算表达式
    function Calc(const AFormulaStr : string) : Double;
  end;
implementation

{ TFormula }
function TFormula.CustomStrToDouble(const Key : string): Double;
begin
  Result := 0;

  if isNumber(Key) then   //如果是数值, 直接返回
    Result := StrToFloat(Key)
  else
  if not ConstValue.TryGetValue(Key, Result) then  //如果是常量则返回, 否则报出异常
    raise Exception.Create('无法识别的数值!');
end;

function TFormula.CalcItem(FExp : TList<string>): Double;
var
  i: integer;
  val: string;
  stack: TStack<Double>; //堆栈;
  d1, d2: Double;
begin
  Result := 0;
  stack := TStack<Double>.create;  //创建栈对象
  try
    if FExp.Count = 1 then  //只有一个数据项时, 直接转换结果
    begin
      Result := CustomStrToDouble(FExp.Items[0]);
    end
    else
    begin
      for i := 0 to FExp.Count - 1 do   //逐项计算
      begin
        val := FExp.Items[i];
        if (Length(val) = 1) and (val[1] in OPEFORCALC) then  //是运算符则计算, 反之压栈
        begin
          d2 := stack.Pop;
          d1 := stack.Pop;
          Result := GetValue(val[1], d1, d2); //根据运算符和数据项计算结果
          stack.push(Result);   //计算结果压栈
        end
        else
          stack.push(CustomStrToDouble(val));
      end;
    end;
  finally
    stack.Free;
  end;
end;
function TFormula.Calc(const AFormulaStr: string): Double;
var
  FExp : TList<string>; // 公式分项列表, 后缀表达式  用于表达式计算
begin
  result := 0;
  if sametext(AFormulaStr, '') then
    raise Exception.Create('无效的表达式');

  FExp := TList<string>.Create;
  try
    try
      splitExp(FExp , AFormulaStr); //产生后缀表达式
      result := CalcItem(FExp);  //计算后缀表达式

    except on E: Exception do
      raise Exception.Create(e.Message);
    end;
  finally
    FExp.Free;
  end;
end;

constructor TFormula.Create;
begin
  //创建常量表, 并初始化
  ConstValue := TDictionary<string, Double>.Create;
  ConstValue.Add('∏', 3.1415926);
end;

destructor TFormula.Destroy;
begin
  ConstValue.Free;  //销毁常量表
  inherited;
end;

function TFormula.GetValue(op: Char; const val1, val2: Double): Double;
begin
  case op of
    '+':
      Result := val1 + val2;
    '-':
      Result := val1 - val2;
    '*':
      Result := val1 * val2;
    '/':
      begin
        if val2 = 0 then
          Result := 0
        else
          Result := val1 / val2;
      end;
  end;
end;

function TFormula.isNumber(const s: string): boolean;
var
  i: Extended;
  Code: integer;
begin
  val(s, i, Code);
  Result := Code = 0;
end;

function TFormula.IsOperand(ch: Char): boolean;
begin
  Result := not(ch in OPE);
end;

function TFormula.Priority(ch: Char): integer;
begin
  case ch of
    '+':
      Result := 1;
    '-':
      Result := 1;
    '*':
      Result := 2;
    '/':
      Result := 2;
  else
    Result := 0;
  end;
end;

procedure TFormula.splitExp(FExp : TList<string>; const sValue: string);
  function AddToExp(const Value : string): string;
  begin
    Result := '';
    if Value = '' then Exit;

    FExp.Add(Value);
  end;
var
  ch, ch1: Char;
  stack: TStack<char>; // 堆栈;
  i: integer;
  sOperand: string;

begin
  // 利用集合模拟堆栈操作,产生后缀表达式

  stack := TStack<char>.create;
  try
    sOperand := '';
    for i := 1 to Length(sValue) do
    begin
      if sValue[i] = chr(32) then
        Continue; // 去除多余的空字符

      ch := sValue[i];
      if IsOperand(ch) then // 如果是操作数,直接放入B中
      begin
        sOperand := sOperand + ch;
      end
      else
      begin
        sOperand := AddToExp(sOperand);
        if ch = '(' then // 如果是'(',将它放入堆栈中
        begin
          stack.Push(ch);
        end
        else if ch = ')' then // 如果是')'
        begin
          while stack.Count > 0 do // 不停地弹出堆栈中的内容,直到遇到'('
          begin
            ch := stack.Pop;
            if ch = '(' then
              break
            else
            begin
              FExp.Add(ch);
            end;
          end;
        end
        else // 既不是'(',也不是')',是其它操作符,比如+, -, *, /之类的
        begin
          if stack.Count > 0 then
          begin
            while stack.Count > 0 do
            begin
              ch1 := stack.Pop; // 弹出栈顶元素
              if Priority(ch) > Priority(ch1) then // 如果栈顶元素的优先级小于读取到的操作符
              begin
                stack.push(ch1); // 将栈顶元素放回堆栈
                stack.push(ch); // 将读取到的操作符放回堆栈
                break;
              end
              else // 如果栈顶元素的优先级比较高或者两者相等时
              begin
                FExp.Add(ch1);
                if stack.Count = 0 then
                begin
                  stack.push(ch); // 将读取到的操作符压入堆栈中
                  break;
                end;
              end;
            end;
          end
          else
            stack.push(ch); // 如果堆栈为空,就把操作符放入堆栈中
        end;
      end;
    end;
    AddToExp(sOperand);

    while stack.Count > 0 do
      AddToExp(stack.Pop);

  finally
    stack.Free;
  end;
end;
end.

//调用单元
unit utForm;

interface

uses
  SysUtils, Classes, Controls, Forms, Dialogs, StdCtrls, ComCtrls, Buttons, ToolWin;

type
  TFrmMain = class(TForm)
    grp1: TGroupBox;
    grp2: TGroupBox;
    mmoFormula: TMemo;
    mmoValue: TMemo;
    tlb1: TToolBar;
    btnCalc: TSpeedButton;
    procedure btnCalcClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  FrmMain: TFrmMain;

implementation
uses utFormula;

{$R *.dfm}
procedure TFrmMain.btnCalcClick(Sender: TObject);
var
  AFormula : TFormula;
  i : integer;
  Value : Double;
begin
  mmoValue.Clear;
  AFormula := TFormula.Create;
  try
    for i := 0 to mmoFormula.Lines.Count - 1 do
    begin
      try
        //表达式计算
        Value := AFormula.Calc(mmoFormula.Lines.Strings[i]);
        //打印结果
        mmoValue.Lines.Add(mmoFormula.Lines.Strings[i] + '=' + FloatToStr(Value));
      except
        on E: Exception do  //异常处理
        begin
          ShowMessage(e.Message);
          mmoValue.Lines.Add(mmoFormula.Lines.Strings[i] + '=?????????');
        end;
      end;
    end;
  finally
    AFormula.Free;
  end;
end;



end.

//调用单元窗体代码
object FrmMain: TFrmMain
  Left = 0
  Top = 0
  Caption = #34920#36798#24335#35745#31639
  ClientHeight = 479
  ClientWidth = 735
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object grp1: TGroupBox
    Left = 0
    Top = 29
    Width = 392
    Height = 450
    Align = alLeft
    Caption = #34920#36798#24335
    TabOrder = 0
    object mmoFormula: TMemo
      Left = 2
      Top = 15
      Width = 388
      Height = 433
      Align = alClient
      Lines.Strings = (
        '1+2'
        '2*2'
        '(1+2*3-3)/2'
        #8719'+1')
      TabOrder = 0
      ExplicitLeft = 45
      ExplicitWidth = 341
    end
  end
  object grp2: TGroupBox
    Left = 392
    Top = 29
    Width = 343
    Height = 450
    Align = alClient
    Caption = #35745#31639#32467#26524
    TabOrder = 1
    ExplicitTop = 0
    ExplicitWidth = 249
    ExplicitHeight = 479
    object mmoValue: TMemo
      Left = 2
      Top = 15
      Width = 339
      Height = 433
      Align = alClient
      TabOrder = 0
      ExplicitTop = 72
      ExplicitWidth = 181
      ExplicitHeight = 405
    end
  end
  object tlb1: TToolBar
    Left = 0
    Top = 0
    Width = 735
    Height = 29
    ButtonHeight = 26
    ButtonWidth = 121
    Caption = 'tlb1'
    TabOrder = 2
    ExplicitLeft = 376
    ExplicitWidth = 150
    object btnCalc: TSpeedButton
      Left = 0
      Top = 0
      Width = 128
      Height = 26
      BiDiMode = bdLeftToRight
      Caption = #24320#22987#35745#31639
      ParentBiDiMode = False
      OnClick = btnCalcClick
    end
  end
end

最重要的一点:从本版开始本软件全部功能完全免费使用。 【一 概述】 易算数学公式计算器 易算数学公式计算器(易算器)是一款非常简单好用的公式计算器,您可以使 用并保存带变量的公式,在运行时只需要在变量列表中填入数据,就可以生 成结果列表,相信使用它可以减少您许多重复工作。 1 可以计算一般代数式,比如“1 + cos(2)”。 2 可在表达式中使用变量,包括中文变量名,比如“电压=电阻*电流”。在 求值时,软件自动提示输入变量的值并进行计算。 3 支持常用函数如三角函数、对数、乘方等,及逻辑函数如>, 大于 (1>0 )=1 < 小于 (1<0 )=0 <= 不大于 (1= 不小于 (1>=0)=1 == 等于 (1==0)=0 不等于 (10)=1 5 逻辑运算符 &或and 字节与 8 and 7 =7 |或or 字节或 8 or 7 =15 !或not 逻辑非 not 1 =0 6 补充公式 max max(a,b) 求a,b两数较大值 min min(a,b) 求a,b两数较小值 T2E_S(T) 已知温度T,求S型热电偶热电势(单位:mV) T2E_N(T) 已知温度T,求N型热电偶热电势(单位:mV) T2E_R(T) 已知温度T,求R型热电偶热电势(单位:mV) T2E_E(T) 已知温度T,求E型热电偶热电势(单位:mV) T2E_B(T) 已知温度T,求B型热电偶热电势(单位:mV) T2E_J(T) 已知温度T,求J型热电偶热电势(单位:mV) T2E_K(T) 已知温度T,求K型热电偶热电势(单位:mV) T2E_T(T) 已知温度T,求T型热电偶热电势(单位:mV) T2E_WRE325(T) 已知温度T,求WRE-325型热电偶热电势(单位:mV) T2E_WRE526(T) 已知温度T,求WRE-526型热电偶热电势(单位:mV) E2T_S(E) 已知热电势E,求S型热电偶温度(单位:℃) E2T_N(E) 已知热电势E,求N型热电偶温度(单位:℃) E2T_R(E) 已知热电势E,求R型热电偶温度(单位:℃) E2T_E(E) 已知热电势E,求E型热电偶温度(单位:℃) E2T_B(E) 已知热电势E,求B型热电偶温度(单位:℃) E2T_J(E) 已知热电势E,求J型热电偶温度(单位:℃) E2T_K(E) 已知热电势E,求K型热电偶温度(单位:℃) E2T_T(E) 已知热电势E,求T型热电偶温度(单位:℃) E2T_WRE325(E) 已知热电势E,求WRE-325型热电偶温度(单位:℃) E2T_WRE526(E) 已知热电势E,求WRE-526型热电偶温度(单位:℃) T2R_PT10(T) 已知温度T,求PT10型热电阻电阻值(单位:Ω) T2R_PT100(T) 已知温度T,求PT100型热电阻电阻值(单位:Ω) T2R_PT1000(T) 已知温度T,求PT1000型热电阻电阻值(单位:Ω) T2R_CU50(T) 已知温度T,求CU50型热电阻电阻值(单位:Ω) T2R_CU100(T) 已知温度T,求CU100型热电阻电阻值(单位:Ω) T2R_PT10(R) 已知电阻R,求PT10型热电阻温度(单位:℃) T2R_PT100(R) 已知电阻R,求PT100型热电阻温度(单位:℃) T2R_PT1000(R) 已知电阻R,求PT1000型热电阻温度(单位:℃) T2R_CU50(R) 已知电阻R,求CU50型热电阻温度(单位:℃) T2R_CU100(R) 已知电阻R,求CU100型热电阻温度(单位:℃) 【四 补充说明】 1 计算按钮快捷键为 ALT+ENTER ,在无变量情况下便于全键盘操作 2 计算结果 出现"INF"表示溢出 出现"Invalid" 表示无意义结果 出现"公式错误" 请检查您的公式 3 关于优先级 扩展数学运算符,三角函数,负号>逻辑非>乘方 >数学运算符>关系运算符>逻辑运算符...... 如碰到不确定优先级的情况下,请用括号码()来确保优先顺序。 4 数值格式 如需输入二进制,请以0b开头,中间不能有空格。举例:0b11 即为3 如需输入十六进制,请以0x开头。举例:0x11即为17 科学计数法,中间的'e'表示阶数,如12e-3为0.012,而12e3为12000 5 鼠标中键最小化到托盘,WIN+C唤醒。
public partial class FormMain : Form { public FormMain() { InitializeComponent(); } //记录所要执行的运算 private string operation; //记录用户是否已经输入小数点 private bool isDot; //记录用户是否已经单击等号 private bool isEqual; //被操作数 private double dblacc; //操作数 private double dbldes; //记录运算结果 private double dblresult; private void subReset() { operation = ""; dblacc = 0; dbldes = 0; isDot = false; isEqual = false; tResult.Text = ""; pKey.Focus(); } private void frmMain_Load(object sender, EventArgs e) { Button[] bt = new Button[28]; int i = 0; bt[1] = btNum1; bt[2] = btNum2; bt[3] = btNum3; bt[4] = btnum4; bt[5] = btNum5; bt[6] = btNum6; bt[7] = btNum7; bt[8] = btNum8; bt[9] = btNum9; bt[10] = btNum0; bt[11] = btNeg; bt[12] = btDot; bt[13] = btDiv; bt[14] = btMul; bt[15] = btAdd; bt[16] = btSub; bt[17] = btEqu; bt[18] = btBackspace; bt[19] = btCE; bt[20] = btC; for (i = 1; i <= 20; i++) { bt[i].Click += new EventHandler(this.bt_Click); } subReset(); (pKey as Control).KeyPress += new KeyPressEventHandler(pKey_KeyPress); } private void frmMain_Activated(object sender, EventArgs e) { pKey.Focus(); } private void bt_Click(object sender, EventArgs e) { string stext = null; Button bclick = new Button(); bclick = sender as Button; stext = bclick.Text; switch (stext) { case "1": case "2": case "3": case "4": case "5": case "6": case "7": case "8": case "9": case "0": if (isEqual) { tResult.Text = ""; //如果已经执行一次计算,那么再次输入数字时,应清空tresult } isEqual = false; tResult.Text = tResult.Text + stext; //将输入字符累加 break; case ".": if (!isDot) { tResult.Text = tResult.Text + stext; isDot = true; } break; //如果此前有小数点输入,那么这次的小数点输入将被忽略 case "+": case "-": case "*": case "/": if (!string.IsNullOrEmpty(tResult.Text.Trim())) { dblacc = Convert.ToDouble(tResult.Text); } tResult.Text = ""; operation = stext; break; //记下被操作数及操作符 case "=": isDot = false; if (!isEqual) { if (!string.IsNullOrEmpty(tResult.Text.Trim())) { dbldes = Convert.ToDouble(tResult.Text); } } //如果本次对"="的单击是连续的第二次单击,那么操作数不变 isEqual = true; switch (operation) { case "+": dblresult = dblacc + dbldes; break; //执行加法操作 case "-": dblresult = dblacc - dblacc; break; //执行减法操作 case "*": dblresult = dblacc * dbldes; break; //执行乘法操作 case "/": dblresult = dblacc / dbldes; break; //执行除法操作 } //根据操作符的不同执行相应的计算 tResult.Text = dblresult.ToString(); dblacc = dblresult; break; //将计算结果赋给被操作数,以便执行连续的第二次操作 case "+-": if (tResult.Text.Substring(0, 1) == "-") { tResult.Text = tResult.Text.Substring(1, tResult.Text.Length - 1); //如果当前数值为负,则去掉数字最前面的"-"将其变为正数 } else { tResult.Text = "-" + tResult.Text; //如果当前数值为正,则在数字最前面加上"-"将其变为负数 } break; //通过对tresult.text字符串的首位的判断来确定当前数值的正负 case "C": subReset(); break; //清除所有操作数,被操作数及运算结果,即将计算器重置 case "CE": tResult.Text = ""; break; //清除文本框内容 case "BackSpace": if (!string.IsNullOrEmpty(tResult.Text)) { //当tResult.Text非空时执行退格操作,以免出现溢出 if (tResult.Text.Substring(tResult.Text.Length - 1) == ".") { isDot = false; } //当小数点被删除时,将小数点标志位bDot重置 tResult.Text = tResult.Text.Substring(0, tResult.Text.Length - 1); //执行退格操作 } break; } pKey.Focus(); //每次单击按钮后,将焦点重新转移到Panel上 } private void MenuItem4_Click(Object sender, EventArgs e) { FormAbout frm = new FormAbout(); frm.Show(); //显示关于窗口 } private void MenuItem2_Click(object sender, EventArgs e) { Close(); } private void pKey_KeyPress(object sender, KeyPressEventArgs e) { switch (e.KeyChar) { case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '0': //输入为数字 if (isEqual) { tResult.Text = ""; } //如果已经执行过一次计算,那么再次输入数字时,应清空tresult isEqual = false; tResult.Text = tResult.Text + e.KeyChar; break; //将输入的字符累加 case '.': if (!isDot) { tResult.Text = tResult.Text + e.KeyChar; isDot = true; } break; //如果此前已经有小数点输入,那么这次的小数点输入将被忽略 case '+': case '-': case '*': case '/': dblacc = Convert.ToDouble(tResult.Text); tResult.Text = ""; operation = e.KeyChar.ToString(); break; //记下被操作数及操作符 case '=': isDot = false; if (!isEqual) { dbldes = Convert.ToDouble(tResult.Text); } //如果本次输入的“=”是第二次输入,那么操作数不变 isEqual = true; switch (operation) { case "+": dblresult = dblacc + dbldes; break; //执行加法操作 case "-": dblresult = dblacc - dbldes; break; //执行减法操作 case "*": dblresult = dblacc * dbldes; break; //执行乘法操作 case "/": dblresult = dblacc / dbldes; break; //执行除法操作 } //根据操作符的不同执行相应的计算 tResult.Text = dblresult.ToString(); dblacc = dblresult; break; //将计算结果赋给被操作数,以便执行连续的第二次操作 case (char)8: if (!string.IsNullOrEmpty(tResult.Text)) { //当tresult.text非空时执行退格操作,以免出现溢出 if (tResult.Text.Substring(tResult.Text.Length - 1) == ".") { isDot = false; } //当小数点被删掉时,将小数点标志位bdot重置 tResult.Text = tResult.Text.Substring(0, tResult.Text.Length - 1); //执行退格操作 } break; } pKey.Focus(); }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值