首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

任意數學表達式解析計算引擎設計與實現:支援函數嵌套、多變量、遞歸下降解析

     編輯:關於VC++

科學計算、繪圖、配置系統常需動態解析用戶輸入的數學表達式。本文展開一套完整的 遞歸下降解析器 實現,支援:四則運算優先級、括號巢狀、標準數學函數(sin/cos/exp/log/pow...)、函數嵌套、多變量求值、計算過程記錄。核心演算法採用 表達式化簡策略:逐層消除最內層括號 → 處理函數調用 → 計算簡單四則運算 → 替換回原式,直到得到最終數值。

表達式分類與支援語法

支援函數與常數

  • 單參數:sin, cos, tan, asin, acos, atan, exp, log, sign(符號函數)
  • 雙參數:pow[base, exp], log[base, x] —— 必須提供兩個參數
  • 常數:PI=3.1415926, E=2.71828 —— 大寫
  • 變數:X, Y, Z —— 大寫,小寫視為非法
  • 角度制:三角函數參數為度數(非弧度)

核心演算法流程

主流程:逐層化簡


double Evaluate(CString expr) {
    // 1. 預處理:大寫轉小寫、合法性檢查
    Preprocess(expr);
    
    // 2. 迴圈化簡,直到無括號、無函數
    while (HasBracket(expr) || HasFunction(expr)) {
        if (HasBracket(expr)) {
            // 2.1 找最內層括號
            int start, end;
            GetInnermostBracket(expr, start, end);
            // 2.2 遞歸計算括號內表達式
            double val = EvaluateNoBracket(expr.Mid(start+1, end-start-1));
            // 2.3 替換回原式
            expr = ReplaceRange(expr, start, end, FormatValue(val));
        } else {
            // 2.4 處理最內層函數
            int fnStart, fnEnd, paramStart, paramEnd;
            GetInnermostFunction(expr, fnStart, fnEnd, paramStart, paramEnd);
            CString funcName = expr.Mid(fnStart, fnEnd-fnStart+1);
            CString paramExpr = expr.Mid(paramStart+1, paramEnd-paramStart-1);
            double paramVal = EvaluateNoBracket(paramExpr);
            double result = CallFunction(funcName, paramVal);
            expr = ReplaceRange(expr, fnStart, paramEnd, FormatValue(result));
        }
    }
    
    // 3. 純四則運算最終計算
    return EvaluateSimpleArithmetic(expr);
}

關鍵輔助函數

類別語法範例說明
簡單表達式1+2*3, 10/2-3僅四則運算,無括號函數
函數表達式sin[30], exp[2+3], sin[1+sin[2]]函數用 [] 包裹參數,支援嵌套
一般表達式(1+sin[30])*3, 2+sin[1+(sin[30]+1)*2]括號+函數+四則運算任意組合

四則運算計算器(運算子優先級)


double CalcArithmetic(CString expr) {
    // 處理優先級:* / 先於 + -
    // 掃描策略:左到右,先處理 * /,再處理 + -
    
    // 1. 處理乘除
    while (HasMulDiv(expr)) {
        int pos = FindFirstMulDiv(expr);
        CString left, right;
        GetOperands(expr, pos, left, right);
        double l = _ttof(left), r = _ttof(right);
        double res = (expr[pos]=='*') ? l*r : l/r;
        expr = ReplaceOperands(expr, pos, left, right, FormatValue(res));
    }
    // 2. 處理加減
    while (HasAddSub(expr)) {
        int pos = FindFirstAddSub(expr);
        // 同理計算...
    }
    return _ttof(expr);
}

變量求值介面


// 單變量掃描
double GetResultFrString1(CString expr, CString varName, double varVal) {
    expr.Replace(varName, FormatValue(varVal));
    return Evaluate(expr);
}

// 多變量
double GetResultFrStringN(CString expr, CString* vars, double* vals, int n) {
    for (int i=0; i

實戰驗證案例

案例 1:任意函數波形繪製(配合 DataShow 類)

函數: 100*pow[2,(sin[X])] —— 注意函數嵌套需加括號 (sin[X])

波形1

pow[2, sin[X]] 波形

函數: 100*sign[sin[X]] —— 方波

方波

sign[sin[X]] 方波

函數: 200*sin[X+30]+100*sin[X/2.0] —— 疊加波

疊加波

多頻率疊加波形

案例 2:科學計算器驗證

計算器介面

計算器運行介面

函數功能
HasBracket(str)判斷是否含 ()
GetInnermostBracket(str, &start, &end)定位最內層括號位置
HasFunction(str)判斷是否含函數名
GetInnermostFunction(str, &fnS,&fnE,&pS,&pE)定位最內層函數名與參數括號 []
ReplaceRange(str, start, end, replacement)區間替換字串
EvaluateSimpleArithmetic(str)計算無括號無函數的四則運算

複雜表達式壓力測試


// 多層括號、函數、運算混合
exp[(56-(((((((sin[(1+29)]+3+2.5)+4)+sin[30]+4.5)+6)+7)+8)+9)-10)]+56-sin[30]+4.5
// 計算結果:62.7183

實現細節與健壯性處理

  • 減負數規範化:2--2 → 2+2,避免解析器誤判
  • 步驟記錄:每步化簡寫入 result.txt,step[] 陣列保存最多 100 步供 UI 顯示
  • 錯誤碼體系:非法字元、括號不匹配、函數參數數目錯誤、定義域錯誤(如 pow[-1,0.5])
  • 無堆疊/鏈表:純 CString 操作(Find/Replace/Mid/Format),邏輯直觀、易維護,雖效能略低於移位歸約法但足夠實用

CExpress 類別介面摘要


class CExpress {
public:
    // 核心計算
    double GetResultFrString1(CString expr, CString var, double val);
    double GetResultFrString2(CString expr, CString v1, double v1, CString v2, double v2);
    double GetResultFrStringN(CString expr, CString* vars, double* vals, int n);
    
    // 內部方法
    BOOL CheckExpression(CString& expr);     // 合法性檢查+預處理
    void GetSingleValue(CString str, double& result);    // 簡單四則運算
    void GetSignFuncVal(CString str, double& result);    // 函數計算
    void GetBracketResult(CString str, double& result);  // 括號遞歸
    
    // 計算過程記錄
    CString step[100];
    int stepNum;
    FILE* resultF; // result.txt
};

總結與擴充方向

  1. 本引擎以 字串化簡 + 遞歸下降 實現任意表達式計算,代碼清晰、功能完整、驗證通過 MATLAB 對照
  2. 適用場景:科學計算器、函數繪圖、參數化配置、腳本化業務規則
  3. 擴充建議:
    • 引入 Tokenzier + AST 替代字串替換,大幅提升效能
    • 支援使用者自定義函數、陣列/矩陣運算、複數運算
    • 整合 muParser、exprtk 等成熟庫替換核心
    • 增加編譯期常數摺疊、JIT 代碼生成

作者:莊三少 | 南京航空航天大學能源與動力學院 | Tel: 13512524413 | Email: visualsan@yahoo.cn

配套代碼:含 CExpress 核心類、DataShow 繪圖類、計算器範例、函數繪圖範例

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
輸入表達式本引擎結果MATLAB驗證備註
pow[(log[2,3+2]*2*log[2,3+2]*2),0.5]4.643864.6439log[2,x] = log2(x)
1+2+3/4+sin[30]+log[10,100]6.256.2500MATLAB用弧度,本引擎用角度
6層嵌套 exp[sin[exp[sin[exp[sin[30]]]]]]1.01811.0181極限嵌套正確