【问题标题】:Prefix recursion notation in c++c ++中的前缀递归表示法
【发布时间】:2012-08-19 18:36:07
【问题描述】:

我一直在寻找用波兰语前缀表示法评估表达式的递归解决方案,但没有找到,但我找到了伪代码,我想将它翻译成 C++,但这很难。我在不知道该怎么做的地方写了 BIG LETTERS。请纠正我,我是 java 人,对我来说 C++ 是一团糟,但我无能为力。

int preEval(stack<string> stos){
  string el = "";
  if(stos.empty()){
    return 0;
  }else if(stos.top() IS VALUE){
    string el = stos.top();
    stos.pop();
    return atoi(el.c_str());
  }else if(stos.top() IS OPERATOR){
    int x = preEval(stos);
    int y = preEval(stos);
    return x OPERATOR y;
  }
  return 0;
}

编辑

当我有像 / 10 5 这样的表达式时,堆栈应该假设有元素(从顶部)/10 5,还是 5 10 /?只是问,因为如果我想在 / 10 5 中使用它,我必须以某种方式向后读取字符串。

【问题讨论】:

  • 将我们链接到您找到的原始伪代码,也许是您想要实现的确切描述?
  • 在进入需要替换的大写之前,有一个bug:处理运算符时,需要调用stos.pop()before 递归,否则你将有一个无限循环。
  • 顺便说一句,我认为stos.empty()的情况一定是错误的,否则* 1会解析成功。
  • strtol 可用于查看字符串是否为整数。
  • 好吧,堆栈可能不是合适的结构。我会改用list。实际上,在/ 10 5 中,您应该首先看到/,然后看到10,最后看到5,使用list 会更自然。

标签: c++ parsing expression evaluation


【解决方案1】:

我认为,更好的解决方案是将工作分为两个阶段:词法分析和解析。

在词法分析阶段,您对每个标记进行分类以查看它是运算符(+- 等)还是常量,或者可能是变量。然后将解析后的实体打包到包含类型和附加信息的结构中。

在代码呈现的解析阶段,您使用的不是字符串,而是结构。查看结构,您可以轻松找出它的类型。 (如果您选择构建从公共基础派生的结构层次结构,它可以是结构内的字段或结构的类型。)

其实Java和C++的逻辑应该是一样的。

【讨论】:

    【解决方案2】:

    如果你有这样的功能:

    #include <assert.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <iostream>
    #include <stack>
    #include <string>
    
    using std::stack;
    using std::string;
    using std::cerr;
    
    enum Operator {
      operator_none,
      operator_plus,
      operator_minus
    };
    
    Operator tokenOperator(const string &token)
    {
      if (token=="+") return operator_plus;
      if (token=="-") return operator_minus;
      return operator_none;
    }
    
    int applyOperator(Operator op,int x,int y)
    {
      switch (op) {
        case operator_plus:  return x+y;
        case operator_minus: return x-y;
        case operator_none:
          break;
      }
      assert(false);
      return 0;
    }
    
    bool isValue(const string &token,int &output_value)
    {
      char *end = 0;
      errno=0;
      output_value = strtol(token.c_str(),&end,10);
      if (errno!=0) return false;
      return *end=='\0';
    }
    
    bool isOperator(const string &token,Operator &output_operator)
    {
      output_operator = tokenOperator(token);
      return output_operator!=operator_none;
    }
    

    那么preEval可以这样实现:

    int preEval(stack<string> &stos)
    {
      if (stos.empty()) return 0;
    
      string el = stos.top();
      stos.pop();
    
      int value = 0;
      Operator op = operator_none;
    
      if (isValue(el,value)) return value;
    
      if (isOperator(el,op)) {
        int x = preEval(stos);
        int y = preEval(stos);
        return applyOperator(op,x,y);
      }
    
      return 0;
    }
    

    【讨论】:

    • 当我有像 / 10 5 这样的表达式时,堆栈应该假设有元素(从顶部)/ 10 5,还是 5 10 / ?只是问,因为如果我想在 / 10 5 中使用它,我必须以某种方式向后读取字符串。
    【解决方案3】:
    #include <string>
    #include <map>
    
    using namespace std;
    
    bool is_value(string s) {
        return s.find_first_not_of("0123456789") == string::npos;
    }
    
    int do_add(int x, int y) {
        return x + y;
    }
    
    int do_subtract(int x, int y) {
        return x - y;
    }
    
    // etc.
    
    typedef int (*binary_op)(int, int);   // Give this function pointer type a nice name
    map<string, binary_op> ops;
    
    // Somewhere before the preEval() is ever called
    ops["+"] = do_add;
    ops["-"] = do_subtract;    // etc.
    
    binary_op lookup_op(string s) {
        map<string, binary_op>::const_iterator it = ops.find(s);
        if (it != ops.end()) {
            return *it;
        } else {
            return NULL;
        }
    }
    

    现在,不是单独测试令牌是否是运算符,然后再执行该运算符,而是使用单个函数调用来获取指向需要调用的运算符函数的指针(如果令牌是运算符),否则为 NULL .即:

    }else if(stos.top() IS OPERATOR){
        int x = preEval(stos);
        int y = preEval(stos);
        return x OPERATOR y;
    }
    

    变成

    } else {
        binary_op op = lookup_op(stos.top());
        if (binary_op != NULL) {
            stos.pop();   // This fixes the bug I mentioned in my top comment
            int x = preEval(stos);
            int y = preEval(stos);
            return op(x, y);
        } else {
            syntax_error();
        }
    }
    

    【讨论】:

    • 恕我直言 lookup_op 可能只是 return ops[s];
    • @Vlad:这可行,但每次查找非运算符字符串时,它都会在地图中创建一个新条目。
    • 嗯,没错。可惜map::operator[]是这样定义的。无论如何,错误的输出预计很少发生,所以我会牺牲一些效率来获得更多的清晰度。 (应该遵循关于惯用代码的标准论点和关于过早优化的答案。)
    • 当我有像 / 10 5 这样的表达式时,堆栈应该假设有元素(从顶部)/ 10 5,还是 5 10 / ?只是问,因为如果我想在 / 10 5 中使用它,我必须以某种方式向后读取字符串。
    • @RobertKilar:这两种方法中的一种是行不通的,不是吗?只需读取每个令牌并将其压入堆栈即可。如果字符串是“5 10 /”,那么最后一个将出现在堆栈顶部的标记将是“/”,这就是我们需要的。另外,如果您觉得答案有用,请点赞。
    猜你喜欢
    • 1970-01-01
    • 2020-10-03
    • 1970-01-01
    • 2020-08-14
    • 1970-01-01
    • 2011-09-23
    • 1970-01-01
    • 2016-11-04
    • 2019-04-26
    相关资源
    最近更新 更多