Как использовать IF-ELSE в RPN (обратная нотация Poli sh)? - PullRequest
1 голос
/ 28 мая 2020

Я создал класс 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) "

Итак, мои вопросы, как использовать IF-ELSE в RPN?

Вот мой код: введите здесь описание ссылки

Ответы [ 2 ]

0 голосов
/ 29 мая 2020

я пытаюсь разобрать многопараметрическую функцию, например if \ Max перед RPN.Parse ()

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;
        }
    }
0 голосов
/ 28 мая 2020

Для if(1>2,3/3,2*1) вы сначала оцените три аргумента справа налево и pu sh их результаты в стеке так, чтобы они выглядели так:

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   
  }
}

Что касается многопараметрических функций, особой обработки не требуется. Просто оцените и получите sh все аргументы (обычно справа налево). Реализация функции должна выдать необходимое количество аргументов, а затем pu sh результат (если есть).

...