【问题标题】:C parser recursionC 解析器递归
【发布时间】:2012-08-11 06:14:57
【问题描述】:

我正在用 C 语言编写一个简单的程序,一个仅涉及整数和 "+ - * /" 运算符的 Lisp 算术计算器,我这样做是为了学习,这不是我的作业或类似的东西。

所以我创建了一个函数,可以正确解析类似 (+ 2 3) 的内容,它会输出 5,所以我知道如何处理非嵌套语句,但是当我有类似 (+ (* 2 3) (- 4 2)) 的内容时该怎么办,所以看起来我可以使用递归来解决这个问题,但我真的不知道该怎么做。

我的逻辑如下(伪代码):`

  function parse_line(int n)
    get_input(string);
    if string[n] == '('
     if string[n+1] == operator
      if string[n+3] == number
       result = parseAllNumbers(); //between ( )
       return result; 
    if string[n+3] == '('
     parse_line(n+2);

`

所以我的逻辑在这里是正确的,如果我有(+ (* 2 3) (- 4 2)) 我会计算。 (* 2 3),我将如何计算(- 4 2),然后将这两个结果加在一起

【问题讨论】:

  • (-4 2) 应该是什么意思?
  • 4 - 2 准确地说是 (- 4 2) 与减号和 4 之间的空格。
  • @user1599217 在您的原始帖子中,应该是(-4 2) 还是(- 4 2)
  • 我现在修复的第二个,抱歉,我从Scheme语言中得到了这个结构的想法。
  • 我猜你应该使用递归而不是像堆栈这样的不同的东西?一旦你有了一个解决方案,有时会很有趣地弄清楚你还能如何解决同样的问题。

标签: c recursion lisp


【解决方案1】:

你肯定是在正确的轨道上。

假设我们编写了一个getToken() 函数,它从当前位置读取字符串中的下一个逻辑标记。逻辑标记可以是数字、'('、')' 或四个运算符中的任何一个。然后我们可以递归地计算表达式。

function evaluateExpression(){
    var token = getToken();

    if( isNumber(token)){
        return token;
    }else if( isOpenParen(token)){
        return evaluateExpression();
    }

    var numOne = evaluateExpression();
    var nextToken = null;
    while( !isRightParen(nextToken)){
        nextToken = getToken();
        numOne = evaluate(token, numOne, nextToken);
    }

    return numOne;
}

函数isNumber()isLeftParen() 执行它们所暗示的操作,如果传递给它的标记分别是数字或左括号,则返回true。 evaluate() 函数采用运算符令牌以及两个数字来评估它们。例如,evaluate(+,2,4) 将返回 6evaluate(-,2,4) 将返回 2

【讨论】:

  • 嗯,谢谢你的帮助,但不一定只有 2 个操作数 (+ 2 3 4 5) 这个表达式也是有效的。
  • 别那么轻易放弃,我们可以轻松修改这个...顺便说一句,我不知道这是一个有效的表达式(只有一个运算符)
  • 感谢您的帮助,我不会放弃。这个表达式在 Scheme 中是有效的,这就是我想要的,我实际上正在观看一个关于 scheme 的讲座,并认为这将是一个很好的 C 练习。
  • 好主意,这是锻炼您的编程技能的好方法。我更新了我的答案以提供一些帮助。递归将简单地基于构成有效表达式的规则。在设计你的算法之前,试着理解所有的可能性和有效的陈述。这是递归的完美示例
【解决方案2】:

这是一个使用递归的解决方案:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

#define OPERATOR 0
#define OPEN_PAREN 1
#define CLOSE_PAREN 2
#define NUMBER 3
#define END_OF_EXPR 4

#define BAD_LINE 1
#define GOOD_LINE 0




typedef struct 
{
   unsigned char type;
   char operator;
   int number;
} token;

token tokens[100];
int num_tokens = 0;
int token_counter = 0;

token get_token(void)
{

   token temp;
   if ( token_counter < num_tokens )
   {
      temp = tokens[token_counter];
      token_counter += 1;
   }
   else
   {
      temp.type = END_OF_EXPR;
   }
   return temp;
}


int tokenize(const char *line, token *tokens,int *num_tokens)
{
   token_counter = 0;
   int number_digit = 0;
   token aToken;
   int length = strlen(line);
   char number_array[20];
   *num_tokens = 0;
   int num_open_paren = 0;
   int num_close_paren = 0;
   for (int i = 0; i < length; i++)
   {
      /* ignore whitespace */
      if ( line[i] == ' ' || line[i] == '\t' || line[i] == '\n' || line[i] == '\r' )
      {
         if ( number_digit > 0 )
         {
            number_array[number_digit] = '\0';
            aToken.number = atoi(number_array);
            aToken.type = NUMBER;
            tokens[*num_tokens] = aToken;
            *num_tokens += 1;       
            number_digit = 0;   
         }
      }
      else if ( line[i] == '(')
      {
         aToken.type = OPEN_PAREN;
         tokens[*num_tokens] = aToken;
         *num_tokens += 1;
         num_open_paren += 1;
      }
      else if (line[i] == ')' )
      {
         if ( number_digit > 0 )
         {
            number_array[number_digit] = '\0';
            aToken.number = atoi(number_array);
            aToken.type = NUMBER;
            tokens[*num_tokens] = aToken;
            *num_tokens += 1;           
            number_digit = 0;
         }
         aToken.type = CLOSE_PAREN;
         tokens[*num_tokens] = aToken;
         *num_tokens += 1;
         num_close_paren += 1;
      }
      else if ( line[i] == '*' || line[i] == '+' ||  
           line[i] == '/' || line[i] == '-' )
      {
         aToken.type = OPERATOR;
         aToken.operator = line[i];
         tokens[*num_tokens] = aToken;
         *num_tokens += 1;       
      }
      else if ( isdigit(line[i]) )
      {
         number_array[number_digit] = line[i];
         number_digit += 1;
      }
      else
      {
         printf("%c - the %d character - is illegal\n",line[i],i+1);
         return BAD_LINE;
      }

   }
   if ( num_open_paren == num_close_paren )
   {
      return GOOD_LINE;
   }
   else
   {
      printf("mismatched parentheses\n:%s\n",line);
      return BAD_LINE;
   }
}


int evaluate(char operator, int *operands, int num_operands)
{
   int result = 0;
   switch (operator)
   {    
      case '+':
         for (int i = 0; i < num_operands; i++)
         {
            result += operands[i];
         }
         break;
      case '-':
         result = operands[0];
         for (int i = 1; i < num_operands; i++)
         {
            result -= operands[i];
         }
         break;
      case '*':
         result = operands[0];
         for (int i = 1; i < num_operands; i++)
         {
            result *= operands[i];
         }    
         break;
      case '/':
         result = operands[0];
         for (int i = 1; i < num_operands; i++)
         {
            result /= operands[i];
         }    
         break;   
      default:
         printf("ERROR invalid operator: %c\n",operator);
   }
   return result;
}

int process_expression(void)
{
   int result = 0;
   token current_token = get_token();
   if ( current_token.type != OPERATOR )
   {
      fprintf(stderr,"ERROR: %s expecting operator\n",__func__);
      exit(1);
   }
   char operator = current_token.operator;
   current_token = get_token();
   int operands[200];
   int operands_index = 0;
   while ( current_token.type != CLOSE_PAREN && current_token.type != END_OF_EXPR)
   {
      if ( current_token.type == NUMBER )
      {
         operands[operands_index] = current_token.number;
         operands_index += 1;
      }
      else if ( current_token.type == OPEN_PAREN )
      {
         operands[operands_index] = process_expression();
         operands_index += 1;
      }
      current_token = get_token();
   }
   result = evaluate(operator,operands,operands_index);
   return result;
}

void process_lisp_string(const char *line)
{

   int result = tokenize(line, tokens,&num_tokens);

   if ( result == GOOD_LINE )
   {
      token first = get_token();
      if ( first.type == OPEN_PAREN )
         printf("the answer for %s is: %d\n",line, process_expression());
      else
      {
         fprintf(stderr,"ERROR: no open parentheses at front of expression\n");
         fprintf(stderr,"token type:%d\n",first.type);
      }
   }
   else 
   {
      printf("the line contained errors\n");
   }
}

int main(const int argc, const char *const argv[])
{
   char *test = "(+ 2 2 )";
   char *line = "(+ (+ 30 20) 2 2)";
   char *line2 = "(- (+ 1000 10) 200)";
   char *line3 = "(- (+ 1000 10) (- 200 10) (* 2 4))";
   char *line4 = "(+ (+ 10 10) (* 2 4) (* 2 3) (* 2 (- 3 1)))";
   process_lisp_string(test);
   process_lisp_string(line);
   process_lisp_string(line2);
   process_lisp_string(line3);
   process_lisp_string(line4);
}

输出:

the answer for (+ 2 2 ) is: 4
the answer for (+ (+ 30 20) 2 2) is: 54
the answer for (- (+ 1000 10) 200) is: 810
the answer for (- (+ 1000 10) (- 200 10) (* 2 4)) is: 812
the answer for (+ (+ 10 10) (* 2 4) (* 2 3) (* 2 (- 3 1))) is: 38

【讨论】:

  • 哇,谢谢这不是作业,我选择C是因为我想练习一下......这很有帮助
【解决方案3】:

C 是最难使用的语言。除非这是家庭作业,否则最好先用另一种高级语言进行尝试。 C 缺少字符串和堆栈等数据结构。正如其他海报所建议的那样,这种处理可能更优雅地递归完成,但我发现非递归更容易。这是一个只处理正整数并且缺少一些错误处理的部分解决方案:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

#define OPERATOR 0
#define OPEN_PAREN 1
#define CLOSE_PAREN 2
#define NUMBER 3

#define BAD_LINE 1
#define GOOD_LINE 0

typedef struct 
{
   unsigned char type;
   char operator;
   int number;
} token;




int get_tokens(const char *line, token *tokens,int *num_tokens)
{
   int number_digit = 0;
   token aToken;
   int length = strlen(line);
   char number_array[20];
   *num_tokens = 0;
   int num_open_paren = 0;
   int num_close_paren = 0;
   for (int i = 0; i < length; i++)
   {
      /* ignore whitespace */
      if ( line[i] == ' ' || line[i] == '\t' || line[i] == '\n' || line[i] == '\r' )
      {
         if ( number_digit > 0 )
         {
            number_array[number_digit] = '\0';
            aToken.number = atoi(number_array);
            aToken.type = NUMBER;
            tokens[*num_tokens] = aToken;
            *num_tokens += 1;       
            number_digit = 0;   
         }
      }
      else if ( line[i] == '(')
      {
         aToken.type = OPEN_PAREN;
         tokens[*num_tokens] = aToken;
         *num_tokens += 1;
         num_open_paren += 1;
      }
      else if (line[i] == ')' )
      {
         if ( number_digit > 0 )
         {
            number_array[number_digit] = '\0';
            aToken.number = atoi(number_array);
            aToken.type = NUMBER;
            tokens[*num_tokens] = aToken;
            *num_tokens += 1;           
            number_digit = 0;
         }
         aToken.type = CLOSE_PAREN;
         tokens[*num_tokens] = aToken;
         *num_tokens += 1;
         num_close_paren += 1;
      }
      else if ( line[i] == '*' || line[i] == '+' ||  
           line[i] == '/' || line[i] == '-' )
      {
         aToken.type = OPERATOR;
         aToken.operator = line[i];
         tokens[*num_tokens] = aToken;
         *num_tokens += 1;       
      }
      else if ( isdigit(line[i]) )
      {
         number_array[number_digit] = line[i];
         number_digit += 1;
      }
      else
      {
         printf("%c - the %d character - is illegal\n",line[i],i+1);
         return BAD_LINE;
      }

   }
   if ( num_open_paren == num_close_paren )
   {
      return GOOD_LINE;
   }
   else
   {
      printf("mismatched parentheses\n:%s\n",line);
      return BAD_LINE;
   }
}

int process_expression(char operator, int *operands, int num_operands)
{
   int result = 0;
   switch (operator)
   {    
      case '+':
         for (int i = 0; i < num_operands; i++)
         {
            result += operands[i];
         }
         break;
      case '-':
         result = operands[0];
         for (int i = 1; i < num_operands; i++)
         {
            result -= operands[i];
         }
         break;
      case '*':
         result = operands[0];
         for (int i = 1; i < num_operands; i++)
         {
            result *= operands[i];
         }    
         break;
      case '/':
         result = operands[0];
         for (int i = 1; i < num_operands; i++)
         {
            result /= operands[i];
         }    
         break;   
      default:
         printf("ERROR invalid operator: %c\n",operator);
   }
   return result;
}

int process_tokens(token *tokens, int num_tokens)
{
   int result = 0;
   /* operators "stack" */
   char operators[100];
   /* "pointer" for current operator */
   int operator_index = -1;
   /* operands "stack" 1 row for each operator (set of parentheses) */
   int operands[100][20];
   /* how many operands for current expression? (current operator) */
   int expression_operands[100];
   for (int i = 0; i < num_tokens; i++)
   {
      if ( tokens[i].type == OPEN_PAREN )
      {
         operator_index += 1;
         expression_operands[operator_index] = 0;
      }
      else if ( tokens[i].type == CLOSE_PAREN )
      {
         result = process_expression(operators[operator_index], 
                                          &operands[operator_index][0],
                                          expression_operands[operator_index]);
         operator_index -= 1;
         if ( operator_index > -1 )
         {
            operands[operator_index][ expression_operands[operator_index] ] = result;
            expression_operands[operator_index] += 1;
         }
      }
      else if ( tokens[i].type == OPERATOR )
      {
         operators[operator_index] = tokens[i].operator;
      }
      else if ( tokens[i].type == NUMBER )
      {
         operands[operator_index][ expression_operands[operator_index] ] = tokens[i].number;
         expression_operands[operator_index] += 1;
      }
   }
   return result;
}

void process_lisp_string(const char *string)
{
   token tokens[100];
   int num_tokens = 0;
   int result = get_tokens(string, tokens,&num_tokens);

   if ( result == GOOD_LINE )
   {
      printf("the answer is: %d\n",process_tokens(tokens,num_tokens));
   }
   else 
   {
      printf("the string contained errors\n");
   }
}

int main(const int argc, const char *const argv[])
{

   char *line = "(+ (+ 30 20) 2 2)";
   char *line2 = "(- (+ 1000 10) 200)";
   char *line3 = "(- (+ 1000 10) (- 200 10) (* 2 4))";
   char *line4 = "(+ (+ 10 10) (* 2 4) (* 2 3) (* 2 (- 3 1)))";
   process_lisp_string(line);
   process_lisp_string(line2);
   process_lisp_string(line3);
   process_lisp_string(line4);

}

结果:

the answer is: 54
the answer is: 810
the answer is: 812
the answer is: 38

【讨论】:

    猜你喜欢
    • 2018-09-30
    • 1970-01-01
    • 2014-04-14
    • 1970-01-01
    • 1970-01-01
    • 2020-05-31
    • 2015-08-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多