使用有限的工具、没有数组、没有库函数计算简单的数学表达式
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()
进行输入,之后我就卡住了。如果不使用更多“工具”,我不知道我应该做什么。
解决方案非常简单,但对于初学者来说可能并不明显。我不会提供完整的程序,而是概述仅使用几个变量实现此程序所需的步骤。
首先要注意两点:
- 您的输入只能包含
-
、+
之一或任意数字(0123456789
)。
getchar()
函数会一次读取输入的一个字符,当输入结束或发生错误时returnEOF
[=81] =]
现在,解决方案:
从循环读取一个字符开始。只有到达输入末尾或发生错误时才会停止:
int c;
while ((c = getchar()) != EOF) {
// logic here
}
从将累加器设置为 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
中]
每次遇到-
或+
时,记住运算,每次遇到运算符,先执行前一次运算,并重置累加器。
// 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
}
当您到达输入的末尾(即退出循环)时,执行最后一个操作(与第 3 点的 if
中的相同逻辑)。
输出结果:
你通常会这样写:
printf("%d\n", result);
但是,如果您不能使用字符串文字 ("%d\n"
) 或 printf()
函数,则必须使用 putchar()
手动执行此操作。这基本上与我们之前将数字扫描到累加器中所做的相反。
如果需要先打印符号,使值为正:
if (result < 0) {
putchar('-');
result = -result;
}
求出小于你的数字的10的最大次方:
int div = 1;
while (result / div / 10)
div *= 10;
用除法和取模10提取和打印每个数字的权力:
while (div) {
putchar('0' + ((result / div) % 10));
div /= 10;
}
注意:开头的'0' +
用于将数字(从0到10)转换成相对的ASCII字符。
以换行结束:
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);
}
这是我大学去年第一次“编程入门”考试的题目:
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()
进行输入,之后我就卡住了。如果不使用更多“工具”,我不知道我应该做什么。
解决方案非常简单,但对于初学者来说可能并不明显。我不会提供完整的程序,而是概述仅使用几个变量实现此程序所需的步骤。
首先要注意两点:
- 您的输入只能包含
-
、+
之一或任意数字(0123456789
)。 getchar()
函数会一次读取输入的一个字符,当输入结束或发生错误时returnEOF
[=81] =]
现在,解决方案:
从循环读取一个字符开始。只有到达输入末尾或发生错误时才会停止:
int c; while ((c = getchar()) != EOF) { // logic here }
从将累加器设置为
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
中]每次遇到
-
或+
时,记住运算,每次遇到运算符,先执行前一次运算,并重置累加器。// 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 }
当您到达输入的末尾(即退出循环)时,执行最后一个操作(与第 3 点的
if
中的相同逻辑)。输出结果:
你通常会这样写:
printf("%d\n", result);
但是,如果您不能使用字符串文字 (
"%d\n"
) 或printf()
函数,则必须使用putchar()
手动执行此操作。这基本上与我们之前将数字扫描到累加器中所做的相反。如果需要先打印符号,使值为正:
if (result < 0) { putchar('-'); result = -result; }
求出小于你的数字的10的最大次方:
int div = 1; while (result / div / 10) div *= 10;
用除法和取模10提取和打印每个数字的权力:
while (div) { putchar('0' + ((result / div) % 10)); div /= 10; }
注意:开头的
'0' +
用于将数字(从0到10)转换成相对的ASCII字符。以换行结束:
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);
}