C解析器递归

时间:2012-08-14 21:29:51

标签: c recursion lisp

我在C和Lisp算术计算器中创建一个简单的程序,只涉及整数和"+ - * /"运算符,我这样做是为了学习目的,这不是我的作业或类似的东西。

所以我已经创建了一个函数来正确地解析它会输出5的这个(+ 2 3),所以我知道如何处理非嵌套语句但是当我有类似(+ (* 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),然后将这两个结果一起添加

3 个答案:

答案 0 :(得分:5)

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

假设我们编写了一个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

答案 1 :(得分: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

答案 2 :(得分:1)

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