使用有限的工具、没有数组、没有库函数计算简单的数学表达式

Evaluate a simple mathematical expression with limited tools, no arrays, and no library functions

这是我大学去年第一次“编程入门”考试的题目:

Using the getchar() function read an input sequence consisting of numbers, + and - signs. The output should be the result of those arithmetical operations.

例如,如果输入是10+13-12+25-5+100,输出应该是131

现在,鉴于我在上大学之前有一点 C 经验,这个问题似乎很容易使用指针、数组等来解决

但这里有一个问题:在考试中你只能使用学生到目前为止所学的东西。并且考虑到这次考试只在开始一个月后学年,你的选择相当有限。

您只能使用变量、基本input/output东西、运算符(逻辑和按位)、条件语句和循环、函数

这意味着没有:数组、字符串、指针、递归、结构或基本上任何其他使这变得容易的东西。

我到底该怎么做?今天是我第二次花了 3 个小时来解决这个问题。我已经成功解决了它,但只是在“作弊”并使用数组、字符串函数 (strtol) 和指针之后。知道如何按规则解决它对我来说很重要,因为我将在即将到来的考试中遇到类似的东西。

编辑:到目前为止,我的尝试是使用 while 循环结合 getchar() 进行输入,之后我就卡住了。如果不使用更多“工具”,我不知道我应该做什么。

解决方案非常简单,但对于初学者来说可能并不明显。我不会提供完整的程序,而是概述仅使用几个变量实现此程序所需的步骤。

首先要注意两点:

  1. 您的输入只能包含-+之一或任意数字(0123456789)。
  2. getchar()函数会一次读取输入的一个字符,当输入结束或发生错误时returnEOF[=81] =]

现在,解决方案:

  1. 从循环读取一个字符开始。只有到达输入末尾或发生错误时才会停止:

    int c;
    
    while ((c = getchar()) != EOF) {
        // logic here
    }
    
  2. 从将累加器设置为 0 开始,每次遇到数字时都向其“添加”数字。

    // outside the loop
    int acc = 0;
    
    // inside the loop
    if (/* c is a digit */)
            acc = acc * 10 + (c = '0');
    

    提示:/* c is a digit */ 条件可能并不简单,您可以将其放在 -+.[=35= 检查的 else 中]

  3. 每次遇到-+时,记住运算,每次遇到运算符,先执行前一次运算,并重置累加器。

    // outside the loop
    int op = 0;
    int result = 0;
    
    // inside the loop
    if (c == '+' || c == '-') {
        if (op) {
            // there already is a previous operation to complete, do it
            if (op == '+')
                result += acc;
            else
                result -= acc;
        } else {
            // first operation encountered, don't do anything yet
            result = acc;
        }
    
        acc = 0; // reset
        op = c; // remember the current operation for the future
    }
    
  4. 当您到达输入的末尾(即退出循环)时,执行最后一个操作(与第 3 点的 if 中的相同逻辑)。

  5. 输出结果:

    你通常会这样写:

    printf("%d\n", result);
    

    但是,如果您不能使用字符串文字 ("%d\n") 或 printf() 函数,则必须使用 putchar() 手动执行此操作。这基本上与我们之前将数字扫描到累加器中所做的相反。

    1. 如果需要先打印符号,使值为正:

      if (result < 0) {
          putchar('-');
          result = -result;
      }
      
    2. 求出小于你的数字的10的最大次方:

      int div = 1;
      
      while (result / div / 10)
          div *= 10;        
      
    3. 用除法和取模10提取和打印每个数字的权力:

      while (div) {
          putchar('0' + ((result / div) % 10));
          div /= 10;
      }
      

      注意:开头的'0' +用于将数字(从0到10)转换成相对的ASCII字符。

    4. 以换行结束:

      putchar('\n');
      

在编写解析器时,我通常会发现自己“缓冲”了“将要完成”的下一个操作。当输入改变状态时——你正在读取数字,但随后你读取了一个操作——然后你执行“缓冲”操作并缓冲将在未来完成的下一个操作。

示例:

10+13-12
^^        - we read 10
  ^       - result=10  - we buffer that we *will* have to do + in the future
   ^^     - reading 13
     ^    - och we stopped reading numbers!
            we execute _buffered_ operation `+` , so we do result += 13
            and buffer `-` to be done in the future
      ^^  - we read 12
        ^ - och, EOF! we execute buffered operation `-` , so we do result -= 12
          - etc.

代码:

#include <stdio.h>
int main() {
    int result = 0; // represents current result
    int temp = 0; // the temporary number that we read into
    int op = 0; // represents the _next_ operation that _will_ be done
    while (1) {
        int c = getchar();
        switch (c) {
        // we read an operation, so we should calculate _the previous_ operation
        // or this is end of our string
        case '+': case '-': case EOF:
            if (op == 0) {
                // we have nothing so far, so start with first number
                result = temp;
            } else if (op == '+') {
                result += temp;
            } else if (op == '-') { 
                result -= temp;
            }
            // the next operation we will do in future is stored in op
            op = c;
            // current number starts from 0
            temp = 0;
            break;
        case '0': case '1': case '2': case '3': case '4':
        case '5': case '6': case '7': case '8': case '9':
            // read a digit - increment temporary number
            temp *= 10;
            temp += c - '0';
            break;
        }
        // we quit here, the result for last operation is calculated above
        if (c == EOF) {
            break;
        }
    }
    printf("%d\n", result);
   
    // As I see it was mentioned that "%d\n" is a string,
    // here's the simplest algorithm for printing digits in a number.
    // Extract one digit from the greatest position and continue up
    // to the last digit in a number.

    // Take negative numbers and throw them out the window.
    if (result < 0) {
         putchar('-');
         result = -result;
    }
    // Our program currently supports printing numbers up to 10000.
    int divisor = 10000;
    // 000100 should print as 100 - we need to remember we printed non-zero
    int was_not_zero = 0;
    while (divisor != 0) {
        // extract one digit at position from divisor
        int digit = result / divisor % 10;
        // if the digit is not zero, or
        // we already printed anything
        if (digit != 0 || was_not_zero) {
            // print the digit
            putchar(digit + '0');
            was_not_zero = 1;
        }
        // the next digit will be to the right
        divisor /= 10;
    }
    putchar('\n');
}
#include <string.h>
#include <stdio.h>

void operate(int * sum, int * n, char todo) {
    if (todo == 1)        *sum += *n;
    else if (todo == -1)  *sum -= *n;
    printf("%s %d\n", todo == 1 ? "ADD :" : "SUB :", *n); 
    *n = 0; 
}

int main()
{
    char * problem = "10+13-12+25-5+100";
    int len = strlen(problem);
    int i=0;
    char c;
    int n = 0;
    int sum = 0;
    char todo = 1;
    while(i < len)
    {
        c = problem[i++];
        if (c < 48 || c >= 58) 
        {
            // Adds or subtracts previous and prepare next
            operate(&sum, &n, todo);
            if (c == '+') todo = 1; 
            else if  (c == '-') todo = -1; 
        } 
        else
        {
            // Collects an integer
            if (n) n *= 10;
            n += c - 48;
        } 
    }
    operate(&sum, &n, todo); // Last pass

    printf("SUM => %d\n", sum); // => 131
    return 0;
}
#include <stdio.h>

void do_operation(char next_operation, int * result, int * number){
    if (next_operation == '+'){
        *result += *number;
        *number = 0;
    } else if (next_operation == '-'){
        *result -= *number;
        *number = 0;
    } else {
        printf("Unknown operation error.");   
    }
}

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

    char c;
    int number = 0;
    int result = 0;
    char next_operation = '+';

    do {
        c = getchar();
        if( c >= '0' && c <= '9' ){
            number = number * 10 + c - 48;
        } else if (c == '+'){
            do_operation(next_operation, &result, &number);
            next_operation = '+';
        } else if (c == '-'){
            do_operation(next_operation, &result, &number);
            next_operation = '-';
        } else {
            do_operation(next_operation, &result, &number);
        }
    } while (c != '\n');

    printf("%d", result);
}