Merge branch 'master' of http://git.newlifex.com/NewLife/X
大石头 编写于 2018-09-25 18:53:17
X
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace NewLife.Expressions
{
    /// <summary>数学表达式</summary>
    public class MathExpression : RpnExpression
    {
        /// <summary>加法</summary>
        public static readonly Char AddChar = '+';
        /// <summary>减法</summary>
        public static readonly Char SubtractChar = '-';
        /// <summary>乘法</summary>
        public static readonly Char MultiplyChar = '*';
        /// <summary>除法</summary>
        public static readonly Char DivideChar = '/';

        /// <summary>实例化</summary>
        public MathExpression()
        {
            OperationChars = new Char[] { AddChar, SubtractChar, MultiplyChar, DivideChar, LeftBracket, RightBracket };
        }

        /// <summary>计算运算符优先级</summary>
        /// <param name="op"></param>
        /// <returns></returns>
        public override Int32 GetOperationLevel(String op)
        {
            switch (op)
            {
                case "*":
                case "/":
                    return 2;
                case "+":
                case "-":
                    return 1;
                case "(":
                case ")":
                    return 0;
                default:
                    return -1;
            }
        }

        /// <summary>适配和替换</summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        protected override String AdapteAndReplace(String expression)
        {
            if (String.IsNullOrWhiteSpace(expression)) return String.Empty;

            return expression.Replace(" ", "");
        }

        /// <summary>解逆波兰表达式</summary>
        /// <param name="expression">标准逆波兰表达式</param>
        /// <param name="args"></param>
        /// <returns>逆波兰表达式的解</returns>
        public override Object Complie(String expression, params Object[] args)
        {
            if (String.IsNullOrWhiteSpace(expression)) return 0;

            var arr = expression.Split(new Char[] { JoinChar });

            var stack = new Stack<Double>();
            for (var i = 0; i < arr.Length; i++)
            {
                var level = GetOperationLevel(arr[i]);
                if (level < 0)
                {
                    stack.Push(ToDouble(arr[i]));
                }
                else if (level > 0)
                {
                    // 为符号则将数字堆栈后两个数据解压并计算,将计算结果压入堆栈
                    if (stack.Count > 1)
                    {
                        var lastValue = stack.Pop();
                        var firstValue = stack.Pop();
                        var result = ComplieRpnExp(lastValue, firstValue, arr[i]);

                        //压入计算结果
                        stack.Push(result);
                    }
                }
            }

            return stack.Pop();
        }

        /// <summary>是否有效</summary>
        /// <param name="arr"></param>
        /// <returns></returns>
        public override Boolean IsValid(String[] arr)
        {
            if (arr == null || arr.Length == 0) return false;

            var regex = new Regex(@"^\d+$|^\-?\d*\.\d*$");
            for (var index = 0; index < arr.Length; index++)
            {
                if (!regex.IsMatch(arr[index].Trim())) throw new ArgumentException(arr[index]);
            }

            return true;
        }

        /// <summary>转为浮点数</summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public Double ToDouble(String value)
        {
            Double tempValue;

            if (Double.TryParse(value, out tempValue)) return tempValue;

            return 0;
        }

        /// <summary>
        /// 计算逆波兰表达式
        /// </summary>
        /// <param name="last">最后压入数字堆栈的数字</param>
        /// <param name="first">首先压入数字堆栈的数字</param>
        /// <param name="op">操作运算符</param>
        /// <returns>返回计算结果</returns>
        private static Double ComplieRpnExp(Double last, Double first, String op)
        {
            switch (op)
            {
                case "+":
                    return first + last;
                case "-":
                    return first - last;
                case "*":
                    return first * last;
                case "/":
                    return first / last;
                default:
                    return 0;

            }
        }
    }
}