C# Reverse Polish algorithm to parse string 2

This article is  modified based on https://www.codeproject.com/Articles/5875/C-Expression-Parser-using-RPN , most of the source code comes from this page

This article adds trigonometric calculations.

Post the call below

1. Single calculation

string strExpression = "A*1.0+Sin(A*1.2-B)-COS(C)+3/2-TAN(45)/(1+F)+LOG(G)-1.5*D-abs(0-9)";

Dictionary<string, double> keyValuePairs1 = new Dictionary<string, double>()
{
    {"A",1.23 },{"B",1.23},{"C",1.23},{"F",1.23},{"G",1.23},{"D",1.23},
};
using (Tokenizer Tokenizer = new Tokenizer(strExpression))
{
    IEnumerable<IToken> paramsTokens = Tokenizer.ParamTokens;
    var sst = Tokenizer.EvaluateRPN(keyValuePairs1);
    //System.IO.File.WriteAllLines("D:\\1.txt", ss.Select(y => y.ToString()));
    System.Diagnostics.Debug.WriteLine("sst=" + sst);
}

2. Multiple calculations

string strExpression2 = "A/0.5+B*C";
            Dictionary<string, double[]> keyPairDataList = new Dictionary<string, double[]>();
            int sumCount = 5000;
            keyPairDataList.Add("A", createRandom(1.17906, 0.04953, sumCount));
            keyPairDataList.Add("B", createRandom(1.87478, 0.39739, sumCount));
            keyPairDataList.Add("C", createRandom(2.17914, 0.54957, sumCount));

            List<Dictionary<string, double>> keyValuePairs = new List<Dictionary<string, double>>();
            for (int index = 0; index < sumCount; index++)
            {
                Dictionary<string, double> aa = new Dictionary<string, double>()
                {
                    { "A", keyPairDataList["A"][index]},
                    { "B", keyPairDataList["B"][index]},
                    { "C", keyPairDataList["C"][index]},
                };
                keyValuePairs.Add(aa);
            }
            IEnumerable<object> ss = new List<object>();
            using (Tokenizer Tokenizer = new Tokenizer(strExpression2))
            {
                IEnumerable<IToken> paramsTokens = Tokenizer.ParamTokens;
                ss = Tokenizer.EvaluateRPN(keyValuePairs);
                System.IO.File.WriteAllLines("D:\\1.txt", ss.Select(y => y.ToString()));
            }

Class Diagram:

Abstract class Token implementation:

 /// <summary>
    /// 
    /// </summary>
    [Serializable]
    public abstract class Token : IToken
    {
        protected TokenType tokenType = TokenType.ERR;
        private string szValue = string.Empty;

        /// <summary>
        /// 构造函数
        /// </summary>
        protected Token(string szValue)
        {
            this.szValue = szValue;
        }

        /// <summary>
        /// 返回类型
        /// </summary>
        public TokenType Type
        {
            get { return this.tokenType; }
        }

        /// <summary>
        /// 获取表达式块的值
        /// </summary>
        public string BlockValue
        {
            get { return this.szValue; }
        }

        /// <summary>
        /// 比较符
        /// </summary>
        /// <returns></returns>
        public static bool operator ==(Token left, string right)
        {
            return left.BlockValue == right;
        }

        /// <summary>
        /// 比较符
        /// </summary>
        /// <returns></returns>
        public static bool operator !=(Token left, string right)
        {
            return left.BlockValue != right;
        }

        /// <summary>
        /// 相等
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            if ((obj is BracketsToken) == false)
                return false;
            if (ReferenceEquals(this, obj))
                return true;
            if (ReferenceEquals(obj, null))
                return false;
            return this.szValue == (obj as BracketsToken).szValue;
        }

        /// <summary>
        /// 取得哈希值
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return this.szValue.GetHashCode();
        }

        /// <summary>
        /// 输出
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return string.Format("{0}:{1}", tokenType.ToString(), szValue);
        }

        /// <summary>
        /// 创建Token实例
        /// </summary>
        /// <param name="szValue"></param>
        /// <returns></returns>
        internal static IToken Create(string szValue)
        {
            if (BracketsToken.Map(szValue))
                return new BracketsToken(szValue);
            if (ArithmeticToken.Map(szValue))
                return new ArithmeticToken(szValue);
            if (LogicalToken.Map(szValue))
                return new LogicalToken(szValue);
            if (TriangleToken.Map(szValue))
                return new TriangleToken(szValue);
            if (ConstIntegerToken.Map(szValue))
                return new ConstIntegerToken(szValue);
            if (TimeToken.Map(szValue))
                return new TimeToken(szValue);
            return new ParameterToken(szValue);
        }
    }

All implementations inherit Token and are mainly divided into:

1. Operator class: BracketsToken (brackets), ArithmeticToken (arithmetic operation), LogicalToken (logical operation), TriangleToken (triangle operation)

2. Operand type: ConstIntegerToken (constant calculation), ParameterToken (parameter calculation)

Write 2 interfaces:

1. IOperator operator interface, interface methods include: IOperand Eval(IOperand left, IOperand right);

2. IOperand operand interface, interface attributes include: VarName (parameter name), VarValue (parameter value)

Analysis and realization:

 paramTokens.Clear();
            arrFinalExpr.Clear();
            Stack stkOp = new Stack();
            ArrayList bottomList = new ArrayList();
            foreach (Token token in this)
            {
                string szToken = token.BlockValue.Trim();
                if (string.IsNullOrEmpty(szToken))
                    continue;
                if ((token is IOperator) == false)
                {
                    if (token is ParameterToken)
                    {
                        arrFinalExpr.Add(token as ParameterToken);
                        paramTokens.Add(token);
                        continue;
                    }
                    arrFinalExpr.Add((token as IOperand));
                    continue;
                }
                if (token == "(")
                    stkOp.Push(token);
                else if (token == ")")
                {
                    Token popToken;
                    while (((popToken = (Token)stkOp.Pop())) != "(")
                    {
                        arrFinalExpr.Add(popToken);
                        if (stkOp.Count == 0)
                            throw new Exception("Unmatched braces!");
                    }
                }
                else
                {
                    if (stkOp.Count == 0 || (Token)stkOp.Peek() == "(" || IsHigherPrecOperator(token, (Token)stkOp.Peek()))
                        stkOp.Push(token);
                    else
                    {
                        while (stkOp.Count != 0)
                        {
                            if (IsLowerPrecOperator(token, (Token)stkOp.Peek()) || IsEqualPrecOperator(token, (Token)stkOp.Peek()))
                            {
                                Token szTop = (Token)stkOp.Peek();
                                if (szTop == "(")
                                    break;
                                szTop = (Token)stkOp.Pop();
                                arrFinalExpr.Add((szTop as IOperator));
                            }
                            else
                                break;
                        }
                        stkOp.Push(token);
                    }
                }
            }
            while (stkOp.Count != 0)
            {
                Token szTop = (Token)stkOp.Pop();
                if (szTop == "(")
                    throw new Exception("Unmatched braces");
                arrFinalExpr.Add((szTop as IOperator));
            }

Calculation realization:

 /// <summary>
        /// 执行计算
        /// </summary>
        /// <param name="htValues">参数值</param>
        /// <returns></returns>
        public object EvaluateRPN(Dictionary<string, double> htValues)
        {
            Stack stPad = new Stack();
            foreach (object var in arrFinalExpr)
            {
                object calcVar = var;
                IOperand op1 = null;
                IOperand op2 = null;
                IOperator oprtr = null;
                if (var is ParameterToken && htValues != null)
                    (calcVar as IOperand).VarValue = htValues[(calcVar as IOperand).VarName];
                if (calcVar is IOperand)
                    stPad.Push(calcVar);
                else if (calcVar is IOperator)
                {
                    op2 = (IOperand)stPad.Pop();
                    if ((calcVar is TriangleToken) == false && stPad.Count != 0)
                        op1 = (IOperand)stPad.Pop();
                    oprtr = (IOperator)calcVar;
                    IOperand opRes = oprtr.Eval(op1, op2);
                    stPad.Push(opRes);
                }
            }
            return ((IOperand)stPad.Pop()).VarValue;
        }

Download: https://download.csdn.net/download/fuweiping/15931044 

NET5.0,VS2019

 

Guess you like

Origin blog.csdn.net/fuweiping/article/details/115005767