【问题标题】:How to use IF-ELSE in RPN(Reverse Polish Notation)?如何在 RPN(反向波兰表示法)中使用 IF-ELSE?
【发布时间】:2020-09-15 09:46:36
【问题描述】:

我已经做了一个 RPN 类来计算最终用户输入的字符串 "1.0+3/2-tan(45)/(1+1)+sin(30)*abs(-1)+Abs(-10)"

然后,我要解析条件语句和多参数函数,例如“if(1>2,3/3,2*1)”,“max(1,2,3,4)”

那么,我的问题是如何在 RPN 中使用 IF-ELSE?

这是我的代码:enter link description here

【问题讨论】:

  • 到目前为止你尝试过什么?这将有助于向我们展示您的代码和您面临的具体问题

标签: c# math rpn


【解决方案1】:

我尝试在RPN.Parse()之前解析if\Max等多参数函数

public class MultiParameterFunctionParser
    {
        public readonly List<string> Funcs = new List<string> {"IF", "MAX"};

        public string Parse(string exp)
        {
            while (IsFunction(exp,out var index,out var funcName))//
            {
                var parameters = GetParameters(exp, index, funcName, out var before, out var after);
                var list = GetParameterList(parameters);

                var value = Evaluate(list, funcName);
                exp= $"{before}({value}){after}";
            }

            return exp;
        }

        /// <summary>
        ///  Is Exp Contains a function?
        /// </summary>
        /// <param name="exp"></param>
        /// <param name="index"></param>
        /// <param name="funcName"></param>
        /// <returns></returns>
        private bool IsFunction(string exp, out int index, out string funcName)
        {
            index = -1;
            funcName = "";
            foreach (var func in Funcs)
            {
                var idx = exp.IndexOf($"{func}(", StringComparison.CurrentCultureIgnoreCase);
                if (idx == -1 || idx + 3 >= exp.Length - 1)
                    continue;
                index = idx;
                funcName = func;
                break;
            }

            return index != -1 && index + 3 < exp.Length - 1;
        }

        /// <summary>
        /// Get Parameters' string
        /// </summary>
        /// <param name="exp">8+if(12,sin(90),0)+1.2</param>
        /// <param name="index">2 if's start index</param>
        /// <param name="before">8+</param>
        /// <param name="after">+1.2</param>
        /// <returns>12,sin(90),0</returns>
        private static string GetParameters(string exp,int index, string funcName, out string before, out string after)
        {
            before = exp.Substring(0, index);

            index += funcName.Length + 1;

            var leftCount = 1; // '(' count
            var rightCount = 0;// ')' count
            var results = "";

            while (index < exp.Length && leftCount != rightCount)
            {
                var c = exp[index];
                if (c.Equals('('))
                    leftCount++;
                else if (c.Equals(')'))
                    rightCount++;

                if (leftCount > rightCount)
                    results += c;
                else
                    break;

                index++;
            }

            after = exp.Substring(index + 1, exp.Length - index - 1);

            return results;
        }

        /// <summary>
        /// Parse Parameter string to list. 
        /// </summary>
        /// <param name="exp">MAX(1,-1),1,0</param>
        /// <returns>{"MAX(1,-1)","1","0"}</returns>
        private static List<string> GetParameterList(string exp)
        {
            var count = exp.Length;

            for (var i = count - 1; i > -1 && exp.Length > 0; i--)
            {
                var c = exp[i];
                if (c != ',')
                    continue;

                var after = exp.Substring(i + 1);
                var before = exp.Substring(0,i);

                if (after.Count(a => a == '(').Equals(after.Count(a => a == ')')))
                {
                    exp = before + '#' + after;
                }
            }

            var results = exp.Split('#').ToList();

            return results;
        }

        private static double Evaluate(List<string> parameters, string funcName)
        {
            if (funcName.Equals("MAX", StringComparison.CurrentCultureIgnoreCase))
                return EvaluateMax(parameters);
            if (funcName.Equals("IF", StringComparison.CurrentCultureIgnoreCase))
                return EvaluateIF(parameters);

            return 0;
        }

        private static double EvaluateIF(List<string> parameters)
        {
            if (parameters == null || parameters.Count != 3)
                throw new Exception("EvaluateIF parameters.Count()!=3");

            var results = new List<double>();
            foreach (var parameter in parameters)
            {
                var rpn = new RPN();
                rpn.Parse(parameter);
                var obj = rpn.Evaluate();

                if (obj == null)
                {
                    throw new Exception("EvaluateIF Not Number!");
                }
                if (obj.ToString().Equals("true", StringComparison.CurrentCultureIgnoreCase))
                {
                    results.Add(1);
                }
                else if (obj.ToString().Equals("false", StringComparison.CurrentCultureIgnoreCase))
                {
                    results.Add(-1);
                }
                else
                {
                    if (double.TryParse(obj.ToString(), out var d))
                        results.Add(d);
                    else
                        throw new Exception("EvaluateIF Not Number!");
                }
            }

            return results[0] >= 0 ? results[1] : results[2];
        }

        private static double EvaluateMax(IEnumerable<string> parameters)
        {
            var results = new List<double>();
            foreach (var parameter in parameters)
            {
                var rpn = new RPN();
                rpn.Parse(parameter);
                var obj = rpn.Evaluate();
                if (double.TryParse(obj.ToString(), out var d))
                    results.Add(d);
            }

            return results.Count > 0 ? results.Max() : 0;
        }
    }

【讨论】:

    【解决方案2】:

    对于if(1&gt;2,3/3,2*1),您将首先从右到左评估三个参数并将它们的结果压入堆栈,使其看起来像这样:

    top-of-stack->false
                  1
                  2
    

    然后if 将在 RPN 引擎中实现类似于(伪代码):

    void DoIf()
    {
      if (pop()) // pop result of "if" evaluation
      {
        var result = pop(); // pop "true" result from stack
        pop(); // discard "false" result
        push(result); // push back "true" result
      }
      else
      {
        pop(); // discard "true" result, leaving "false" result on stack   
      }
    }
    

    对于多参数函数,应该不需要特殊处理。只需评估并推送所有参数(通常是从右到左)。函数的实现应该弹出所需数量的参数,然后推送其结果(如果有的话)。

    【讨论】:

    • 如果事实上条件为真,只评估真实情况的论点不是更好吗?毕竟它可能是非终止的!
    • 是的,但这更复杂。
    • 感谢您的回答。我已经在这个问题中发布了代码,它的工作成功
    猜你喜欢
    • 2019-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-13
    • 2016-07-23
    • 1970-01-01
    相关资源
    最近更新 更多