逆ポーランドについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
teru

逆ポーランドについて

#1

投稿記事 by teru » 10年前

C言語初心者です。
やさしいC(アルゴリズム編)P.242〜244の逆ポーランドについて、いくつか質問です。

コード:

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

#define SIZE 50

void init (void) ;
void calc (void);
void push (int n);
int pop (void);
int isEmpty (void);
int isFull(void);

int stack[SIZE];
int sp;

int main (void)
{
    init();    //質問①
    calc();

    return 0;
}

void calc (void)
{
    int token;
    int a,b,c;
    printf ("式を入力してください。¥n");
    while ((token = getchar()) != '¥n'){
        if ( token ==  '¥n' || token == ' '){
            ;    //質問②
        }
        else if ( isdigit(token)){
            ungetc (token, stdin);
            scanf ("%d", &token);
            if (!isFull()) push (token);
        }
        else if (token == '+' || token == '-' || token == '*' || token == '/'){
            if (!isEmpty()) b = pop());
            if (!isEmpty()) a = pop());
            switch (token){
                case '+':
                    if (!isFull()) push (a+b);
                    break;
                case '-':
                    if (!isFull()) push (a-b);
                    break;
                case '*':
                    if (!isFull()) push (a*b);
                    break;
                case '/':
                    if (!isFull()) push (a/b);
                    break;
            }
        }
    }
    if (!isEmpty()) c = pop();
    printf ("計算結果は%dです。¥n",c);
}
void init (void)
{
    sp = ;
}
void push (int n)
{
    stack [sp++] = n;
}
int pop (void)
{
    return stack [--sp];
}
int isFull(void)
{
    if (sp >= SIZE){
        printf("スタックが満杯です。¥n");
        return 1;
    }
    return 0;
}
int isEmpty(void)
{
    if (sp <= 0){
        printf("スタックは空です。¥n");
        return 1;
    }
    return 0;
}
質問①
init()についてですが、関数に何も記入されていない()があるということは何を意味すのでしょうか。

質問②
本来ならば文が入るところに";"のみ入ってるということは、省略か何かなのでしょうか。

質問③
このプログラム全体についての質問です。
式を入力するとき、逆ポーランド記法で入力して計算とのプログラムになっておりますが、
普通の計算式を入力して、その式を逆ポーランドにし、答えを出すプログラムはどのようなものでしょうか。

宜しくお願い致します。

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: 逆ポーランドについて

#2

投稿記事 by みけCAT » 10年前

teru さんが書きました:質問①
init()についてですが、関数に何も記入されていない()があるということは何を意味すのでしょうか。
その関数を引数なしで呼び出すことを意味します。
teru さんが書きました:質問②
本来ならば文が入るところに";"のみ入ってるということは、省略か何かなのでしょうか。
これは空文という、何もしない文です。
teru さんが書きました:質問③
このプログラム全体についての質問です。
式を入力するとき、逆ポーランド記法で入力して計算とのプログラムになっておりますが、
普通の計算式を入力して、その式を逆ポーランドにし、答えを出すプログラムはどのようなものでしょうか。
パーサと呼ばれるものの一種だと思います。
自分のライブラリにあったプログラムを貼っておきます。

コード:

#include <stdio.h>
#include <ctype.h>
#include <limits.h>

#define SIKI_MAX 100

/* more priority, more value(divided by 10) */
#define OP_NULL		(-999)
#define OP_WORKED	(-987)
#define OP_MUL		(-100)
#define OP_DIV		(-101)
#define OP_PLUS		(-200)
#define OP_MINUS	(-201)
#define OP_KAKKO	(-300)

int siki_num;
int siki[SIKI_MAX];
int stack_num;
int stack[SIKI_MAX];

/* if success, returns 1. on error, returns 0. */
int rp_compile(const char* expr) {
	int i;
	int expectop=0;
	siki_num=stack_num=0;
	for(i=0;expr[i];i++) {
		int nowop=OP_NULL;
		switch(expr[i]) {
			case '(':
				if(expectop)return 0;
				stack[stack_num++]=OP_KAKKO;
				expectop=0;
				nowop=OP_WORKED;
				break;
			case ')':
				if(!expectop)return 0;
				while(stack_num>0 && stack[stack_num-1]!=OP_KAKKO) {
					siki[siki_num++]=stack[--stack_num];
				}
				if(stack_num>0)stack_num--; else return 0;
				expectop=1;
				nowop=OP_WORKED;
				break;
			case '*': nowop=OP_MUL;break;
			case '/': nowop=OP_DIV;break;
			case '+': nowop=OP_PLUS;break;
			case '-': nowop=OP_MINUS;break;
		}
		if(nowop!=OP_NULL && nowop!=OP_WORKED) {
			if(!expectop)return 0;
			while(stack_num>0 && stack[stack_num-1]/10>=nowop/10) {
				siki[siki_num++]=stack[--stack_num];
			}
			stack[stack_num++]=nowop;
			expectop=0;
		} else if(nowop!=OP_WORKED) {
			int nowvalue;
			if(expectop)return 0;
			/* if(expr[i]=='0')return 0; */
			for(nowvalue=0;isdigit(expr[i]);i++) {
				nowvalue=nowvalue*10+expr[i]-'0';
			}
			i--;
			/* if(nowvalue==0)return 0; */
			siki[siki_num++]=nowvalue;
			expectop=1;
		}
	}
	if(!expectop)return 0;
	while(stack_num>0) {
		if(stack[stack_num-1]==OP_KAKKO)return 0;
		siki[siki_num++]=stack[--stack_num];
	}
	return 1;
}

/* returns INT_MAX on error */
int rp_calc(void) {
	int i;
	stack_num=0;
	for(i=0;i<siki_num;i++) {
		if(siki[i]>=0)stack[stack_num++]=siki[i];
		else {
			if(stack_num<2)return INT_MAX;
			stack_num--;
			switch(siki[i]) {
				case OP_MUL:   stack[stack_num-1]*=stack[stack_num];break;
				case OP_DIV:
					if(stack[stack_num]==0)return INT_MAX;
					stack[stack_num-1]/=stack[stack_num];
					break;
				case OP_PLUS:  stack[stack_num-1]+=stack[stack_num];break;
				case OP_MINUS: stack[stack_num-1]-=stack[stack_num];break;
				default:       return INT_MAX;
			}
		}
	}
	return stack_num==1?stack[0]:INT_MAX;
}

int main(void) {
	char siki_str[SIKI_MAX];
	while(fgets(siki_str,sizeof(siki_str),stdin)) {
		/* 余計な改行文字を消す */
		char* newline=siki_str;
		for(;*newline;newline++) {
			if(*newline=='\n') {
				*newline='\0';
				break;
			}
		}
		/* 計算する */
		if(rp_compile(siki_str)) {
			printf("%d\n",rp_calc());
		} else {
			puts("compile error");
		}
	}
	return 0;
}
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

かずま

Re: 逆ポーランドについて

#3

投稿記事 by かずま » 10年前

teru さんが書きました: このプログラム全体についての質問です。
そのプログラムは、39行目、40行目、62行目がエラーになります。
teru さんが書きました: 普通の計算式を入力して、その式を逆ポーランドにし、答えを出すプログラムはどのようなものでしょうか。

コード:

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
 
unsigned char c;
const char *p, o[] = "+-*/";
char b[1000], rpn[1600], *r;
double stack[300];
 
int get(void) { while (isspace(c = *p++)); return c; }
 
void expr(const char *s)
{
    int d;
    if (*s)
        for (expr(s+2); c == s[0] || c == s[1]; )
            d = c, expr(s + 2), *r++ = ' ', *r++ = d;
    else if (get() == '.' || isdigit(c))
        r += sprintf(r, " %.16g", strtod(p-1, (char **)&p)), get();
    else if (c == '(') expr(o), c == ')' ? get() : (c = 1);
    else if (c == '+') expr(s);
    else if (c == '-') expr(s), *r++ = '  ', *r++ = '_';
    else c = 1;
}
 
double calc(void)
{
    double *sp = stack;
    while (get())
        if (isdigit(c)) *sp++ = strtod(p-1, (char **)&p);
        else if (c == '+') sp--, sp[-1] += *sp;
        else if (c == '-') sp--, sp[-1] -= *sp;
        else if (c == '*') sp--, sp[-1] *= *sp;
        else if (c == '/') sp--, sp[-1] /= *sp;
        else if (c == '_') sp[-1] = -sp[-1];
    return *--sp;
}
 
int main(void)
{
    while (printf("> "), fgets(b, sizeof b, stdin) && *b != '.')
        p = b, r = rpn, expr(o), *r = 0, c ? puts(" error") :
        (printf(" RPN [%s ]\n", rpn), p = rpn, printf("  %.15g\n", calc()));
    return 0;
}

teru

Re: 逆ポーランドについて

#4

投稿記事 by teru » 10年前

みけCATさん
ありがとうございます。
36行目などの
if(!expectop)return 0;
のかっこ内はもとはどのような文なのでしょうか?

teru

Re: 逆ポーランドについて

#5

投稿記事 by teru » 10年前

かずまさん
ありがとうございます。
まだまだ未熟者なのでプログラムの解説または、コメントがあるとありがたいです。
とても勉強になります。
よろしくお願いいたします。

teru

Re: 逆ポーランドについて

#6

投稿記事 by teru » 10年前

追記
62行目のエラー、私のミスですが、実際には何が入りますか?

teru

Re: 逆ポーランドについて

#7

投稿記事 by teru » 10年前

かずまさんのプログラムでdoubledではなくintにして、=が最後につかないと計算結果が表示されないというプログラムを作ってみました。

コード:

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
 
char c;
char *p, o[] = "+-*/";	
char b[300], rpn[300], *r;
int stack[100];
int *sp;
 
int get(void) {
	while (isspace(c = *p++));	
	return c; 
}
 
void expr(char *s)
{
    int d;	//
    if (*s)
        for (expr(s+2); c == s[0] || c == s[1]; )
            d = c, expr(s + 2), *r++ = ' ', *r++ = d;
    else if (get() == '.' || isdigit(c))
        r = r + sprintf(r, " %.16g", atoi(p-1, (char **)&p)), get();
    else if (c == '(') expr(o), c == ')' ? get() : (c = 1);
    else if (c == '+') expr(s);
    else if (c == '-') expr(s), *r++ = '  ', *r++ = '_';
    else c = 1;
}
 
int calc(void)
{
    int *sp = stack;
    while (get())
        if (isdigit(c)) *sp++ = strtod(p-1, (char **)&p);
        else if (c == '+') sp--, sp[-1] += *sp;
        else if (c == '-') sp--, sp[-1] -= *sp;
        else if (c == '*') sp--, sp[-1] *= *sp;
        else if (c == '/') sp--, sp[-1] /= *sp;
    return *--sp;
}

int main(void)
{
	printf("計算式 ? ");
	if(sp = strstr(b,'=')){
		while (fgets(b, sizeof b, stdin) && *b != '.')
        p = b, r = rpn, expr(o), *r = 0,
        (printf(" RPN [%s ]\n", rpn), p = rpn, printf("結果は  %.15gです\n", calc()));
    return 0;
	}
	else{
		printf("計算できません");

		return 0;
	}
}

エラーは出ませんが、動作が終了してしまいます。
どなた様か、アドバイス頂けたら、、と思います。

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: 逆ポーランドについて

#8

投稿記事 by みけCAT » 10年前

teru さんが書きました:みけCATさん
ありがとうございます。
36行目などの
if(!expectop)return 0;
のかっこ内はもとはどのような文なのでしょうか?
C言語的には「もとの文」は無いと思います。
日本語で書くと、「expectopの論理否定」です。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: 逆ポーランドについて

#9

投稿記事 by みけCAT » 10年前

teru さんが書きました:かずまさんのプログラムでdoubledではなくintにして、=が最後につかないと計算結果が表示されないというプログラムを作ってみました。

(コード省略)

エラーは出ませんが、動作が終了してしまいます。
どなた様か、アドバイス頂けたら、、と思います。
自分の環境ではコンパイルエラーが出ました。
23行目のatoiに渡している引数が多すぎます。
また、45行目のstrstrの第二引数も不自然です。
48行目の2番めのprintfのフォーマット文字列も不自然なようですね。
以下、エラーと警告の内容です。

コード:

YUKI.N>gcc -g3 -Wall -Wextra -o 7_raw.exe 7_raw.c
7_raw.c: In function 'expr':
7_raw.c:23:9: error: too many arguments to function 'atoi'
         r = r + sprintf(r, " %.16g", atoi(p-1, (char **)&p)), get();
         ^
In file included from 7_raw.c:1:0:
C:\MinGW_20140813\include/stdlib.h:178:37: note: declared here
 _CRTIMP int __cdecl __MINGW_NOTHROW atoi (const char*);
                                     ^
7_raw.c:23:9: warning: format '%g' expects argument of type 'double', but argume
nt 3 has type 'int' [-Wformat=]
         r = r + sprintf(r, " %.16g", atoi(p-1, (char **)&p)), get();
         ^
7_raw.c:26:40: warning: multi-character character constant [-Wmultichar]
     else if (c == '-') expr(s), *r++ = '  ', *r++ = '_';
                                        ^
7_raw.c:26:5: warning: overflow in implicit constant conversion [-Woverflow]
     else if (c == '-') expr(s), *r++ = '  ', *r++ = '_';
     ^
7_raw.c: In function 'main':
7_raw.c:45:2: warning: implicit declaration of function 'strstr' [-Wimplicit-fun
ction-declaration]
  if(sp = strstr(b,'=')){
  ^
7_raw.c:45:10: warning: incompatible implicit declaration of built-in function '
strstr' [enabled by default]
  if(sp = strstr(b,'=')){
          ^
7_raw.c:45:2: warning: passing argument 2 of 'strstr' makes pointer from integer
 without a cast [enabled by default]
  if(sp = strstr(b,'=')){
  ^
7_raw.c:45:2: note: expected 'const char *' but argument is of type 'int'
7_raw.c:45:8: warning: assignment from incompatible pointer type [enabled by def
ault]
  if(sp = strstr(b,'=')){
        ^
7_raw.c:45:2: warning: suggest parentheses around assignment used as truth value
 [-Wparentheses]
  if(sp = strstr(b,'=')){
  ^
7_raw.c:48:9: warning: format '%g' expects argument of type 'double', but argume
nt 2 has type 'int' [-Wformat=]
         (printf(" RPN [%s ]\n", rpn), p = rpn, printf("結果は  %.15gです\n", ca
lc()));
         ^
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: 逆ポーランドについて

#10

投稿記事 by みけCAT » 10年前

teru さんが書きました:追記
62行目のエラー、私のミスですが、実際には何が入りますか?
多分

コード:

sp = 0;
だと思います。
正確な表現は、該当の本が手元に無いのでわかりません。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

かずま

Re: 逆ポーランドについて

#11

投稿記事 by かずま » 10年前

if(sp = strstr(b,'=')){

文字列の中から「文字」を検索するなら strchr(b, '=')。
文字列の中から「文字列」を検索するなら strstr(b, "=")。
それ以上に問題なのが、fgets() で b に文字列を読み込む前の
空っぽの b から、'=' を探そうとしていることです。
また、'=' があったとしても、最後にあるとは限りません。

解説とコメントが欲しいということなので、
intにして、=が最後につかないと計算結果が表示されないというプログラム
にコメントを付けてみました。

コード:

#include <stdio.h>   // printf, fgets, puts, sprintf
#include <stdlib.h>  // strtol
#include <ctype.h>   // isspace, isdigit
 
char o[] = "+-*/";   // 演算子(operators)
unsigned char c;     // 入力文字(character)
char *p;             // 次の入力文字へのポインタ(pointer)
char buf[300];       // 数式文字列バッファ(buffer)
char rpn[300];       // RPN文字列バッファ(Reverse Polish Notation)
char *r;             // rpnバッファの書き込みポインタ(rpn pointer)
int stack[100];      // 数値計算用スタック(stack)
 
int get(void)        // 次の入力文字の取得
{
    while (isspace(c = *p++)) ;  // 空白は読みとばす
    return c; 
}
 
void expr(char *b)   // 数式(expression)の処理
{
    int d;
    if (*b)          // 二項演算子(binary operator)がある場合
        for (expr(b+2); c == b[0] || c == b[1]; )
            d = c, expr(b+2), r += sprintf(r, " %c", d);
    else if (get() == '-' || c == '+' || isdigit(c))      // 数値の読み取り
        r += sprintf(r, " %ld", strtol(p-1, &p, 10)), get();
    else if (c == '(') expr(o), c == ')' ? get() : (c = 1);  // (式) の処理
    else c = 1;
}
 
int calc(void)       // RPNの計算(calculation)
{
    int *sp = stack; // スタックポインタの初期化
    while (get())
        if (isdigit(c)) *sp++ = strtol(p-1, &p, 10);
        else if (c == '+') sp--, sp[-1] += *sp;
        else if (c == '-') sp--, sp[-1] -= *sp;
        else if (c == '*') sp--, sp[-1] *= *sp;
        else if (c == '/') sp--, sp[-1] /= *sp;
    return *--sp;
}
 
int main(void)
{
    while (printf("計算式 ? "), fgets(buf, sizeof buf, stdin) && *buf != '.') {
        p = buf;   // bufを指すように読み込みポインタをセット
        r = rpn;   // rpnを指すように書き込みポインタをセット
        expr(o);   // 数式を処理して RPN に変換
        if (c == '=' && get() == 0) {  // 最後に '=' がある場合
            printf(" RPN [%s ]\n", rpn);
            p = rpn;     // rpnを指すように読み込みポインタをセット
            printf("結果は %d です\n", calc());
        else
            puts("計算できません");
    }
    return 0;
}
expr() の呼び出しは、expr(o)、expr(b+2)、expr(b) となっています。

expr(o) は expr("+-*/") です。
b が "+-*/" なら、b+2 は "*/"。
b が "*/" なら、b+2 は ""。
したがって、expr(b+2) は expr("*/") または expr("") です。
b が "" なら、expr(b) は expr("") です。

expr("+-*/") は、乗除式を項とする加減式を処理します。
expr("*/") は、数値または「( 式 )」を項とする乗除式を処理します。
expr("") は、数値または「( 式 )」を処理します。

double の数値を読み取るのは strtod() ですが、
int の数値を読み取るのは strtol() です。

double の数値を出力する書式は "%g" ですが、
int の数値を出力する書式は "%d" です。

最初のプログラムは 単項のプラス演算子とマイナス演算子を扱っていましたが、
今回のプログラムでは、それをやめて、数値にだけ±の符号を許すようにしました。

こんな説明で理解いただけるでしょうか?
分からないところがあれば、また質問してください。

かずま

Re: 逆ポーランドについて

#12

投稿記事 by かずま » 10年前

訂正です。
53行目の else の前に } が必要。

それから、単項演算子を扱わなくなったので、expr(b) の呼び出しはありません。

かずま

Re: 逆ポーランドについて

#13

投稿記事 by かずま » 10年前

バグがありました。

expr()で負の数を扱えるようにしたのですが、
calc()のほうの修正を忘れ、数字で始まるのが数値で
負号は引き算の演算子と解釈されていました。

例えば、-3+5= が [ -3 5 + ] と正しく変換されるのですが、
- で直前に何もないのに引き算が実行され、結果は 3 5 + の 8
になっていました。

修正版ですが、
グローバル変数にする必要のないものをローカル変数にしてみました。

コード:

#include <stdio.h>   // printf, fgets, puts, sprintf
#include <stdlib.h>  // strtol
#include <ctype.h>   // isspace, isdigit
 
char o[] = "+-*/";   // 演算子(operators)
unsigned char c;     // 入力文字(character)
char *p;             // 次の入力文字へのポインタ(pointer)
char *r;             // RPNバッファへの書き込みポインタ(rpn pointer)
 
int get(void)        // 次の入力文字の取得
{
    while (isspace(c = *p++)) ;  // 空白は読みとばす
    return c; 
}
 
void expr(char *b)   // 数式(expression)の処理
{
    int d;
    if (*b)          // 二項演算子(binary operator)がある場合
        for (expr(b+2); c == b[0] || c == b[1]; )
            d = c, expr(b+2), r += sprintf(r, " %c", d);
    else if (get() == '-' || c == '+' || isdigit(c))      // 数値の読み取り
        r += sprintf(r, " %ld", strtol(p-1, &p, 10)), get();
    else if (c == '(') expr(o), c == ')' ? get() : (c = 1);  // (式) の処理
    else c = 1;
}
 
int calc(char *rpn)  // RPNの計算(calculation)
{
    int stack[100];  // 数値計算用スタック(stack)
    int *sp = stack; // スタックポインタの初期化
    for (p = rpn; get(); )
        if (isdigit(c) || c == '-' && isdigit((unsigned char)*p))
            *sp++ = strtol(p-1, &p, 10);
        else if (c == '+') sp--, sp[-1] += *sp;
        else if (c == '-') sp--, sp[-1] -= *sp;
        else if (c == '*') sp--, sp[-1] *= *sp;
        else if (c == '/') sp--, sp[-1] /= *sp;
    return *--sp;
}
 
int trans(char *buf, char *rpn)  // 数式を RPN に変換(translate)
{
    p = buf;         // buf を指すように読み込みポインタをセット
    r = rpn;         // rpn を指すように書き込みポインタをセット
    expr(o);         // 数式を処理して RPN に変換
    return (c == '=') && (get() == 0);  // 最後に '=' があるかどうかを返す
}

int main(void)
{
    char buf[300];  // 数式文字列バッファ(buffer)
    char rpn[600];  // RPN文字列バッファ(Reverse Polish Notation)
    while (printf("計算式 ? "), fgets(buf, sizeof buf, stdin) && *buf != '.')
        if (trans(buf, rpn))  // 数式を RPN に変換できたら
            printf(" RPN [%s ]\n" "結果は %d です\n", rpn, calc(rpn));
        else
            puts("計算できません");
    return 0;
}

teru

Re: 逆ポーランドについて

#14

投稿記事 by teru » 10年前

みけCAT様

返信が遅くなり、申し訳ございません。

sp=0

であってました。

ありがとうございます!

teru

Re: 逆ポーランドについて

#15

投稿記事 by teru » 10年前

かずま様
遅くなり申し訳ございません。

プログラムの19行目から29行目の解説してくださったところがいまいちい理解できていません。
なぜ"b+2"がいきなり出てくるのでしょうか?
初歩的な質問なら申し訳なです。
そこのプログラムをもう少し教えて頂けるとありがたいです。

かずま

Re: 逆ポーランドについて

#16

投稿記事 by かずま » 10年前

teru さんが書きました: プログラムの19行目から29行目の解説してくださったところがいまいちい理解できていません。
なぜ"b+2"がいきなり出てくるのでしょうか?
式は、加減式です。

加減式とは、A + B または A - B のことですが、
- は + と同等ですから、以後 + だけで話を進めます。
加減式は、A + B + C + ... のように + を何回繰り返してもよくて、
0回の A だけでも構いません。

加減式の項である A、B、C は、乗除式です。

乗除式とは、a * b または a / b のことですが、
/ は * と同等ですから、以後 * だけで話を進めます。
乗除式は、a * b * c * ... のように * を何回繰り返してもよくて、
0回の a だけでも構いません。

乗除式の項である a、b、c は、数か、または「( 式 )」です。

このように考えることで、
例えば、a + b * c は、b * c を先に計算するように、
乗除算を加減算より先にするという構文を処理できます。

int expr(char *b) は、次の 3通りの呼び出し方をされます。

1. b = "+-*/" の場合、加減式の処理をします。
2. b = "*/" の場合、乗除式の処理をします。
3. b = "" の場合、数または「( 式 )」の処理をします。

加減式の処理をするとき、その項である乗除式の処理を呼び出して、
それを繰り返し + していかなければなりません。

乗除式の処理をするとき、その項である数または「( 式 )」の処理を呼び出して、
それを繰り返し * していかなければなりません。

すなわち、
b = "+-*/" で呼び出されたら b + 2 である "*/" が必要になります。
b = "*/" で呼び出されたら b + 2 である "" が必要になります。

次のように書けば b + 2 は不要です。

コード:

void expr(char *b)   // 数式(expression)の処理
{
    int d;
    if (*b == '+')   // 加減式の処理
        for (expr("*/"); c == '+' || c == '-'; )  // 第1項の乗除式の処理を呼び出す
            d = c, expr("*/"), r += sprintf(r, " %c", d);  // 第2項の数の処理をよだす
    else if (*b == '*')  // 乗除式の処理
        for (expr(""); c == '*' || c == '/'; )
            d = c, expr(""), r += sprintf(r, " %c", d);
    else if (get() == '-' || c == '+' || isdigit(c))      // 数値の読み取り
        r += sprintf(r, " %ld", strtol(p-1, &p, 10)), get();
    else if (c == '(') expr(o), c == ')' ? get() : (c = 1);  // (式) の処理
    else c = 1;
}
しかし、加減式の処理と乗除式の処理は同じ形をしているので、ひとつにまとめて、
if (*b) の判定で、数の処理ではないことを判断して、
expr(b+2) を呼び出しています。

かずま

Re: 逆ポーランドについて

#17

投稿記事 by かずま » 10年前

コメントが間違ったまま送信してしまいました。次のように訂正します。

コード:

void expr(char *b)   // 数式(expression)の処理
{
    int d;
    if (*b == '+')   // 加減式の処理
        for (expr("*/"); c == '+' || c == '-'; )  // 第1項の乗除式の処理を呼び出す
            d = c, expr("*/"), r += sprintf(r, " %c", d);  // 第2項の乗除式の処理を呼び出す
    else if (*b == '*')  // 乗除式の処理
        for (expr(""); c == '*' || c == '/'; )  // 第1項の数の処理を呼び出す
            d = c, expr(""), r += sprintf(r, " %c", d);  // 第2項の数の処理を呼び出す
    else if (get() == '-' || c == '+' || isdigit(c))      // 数の処理
        r += sprintf(r, " %ld", strtol(p-1, &p, 10)), get();
    else if (c == '(') expr(o), c == ')' ? get() : (c = 1);  // (式) の処理
    else c = 1;
}

teru

Re: 逆ポーランドについて

#18

投稿記事 by teru » 10年前

そういうことだったんですね!
解決しました!
たくさんのコードありがとうございました!

閉鎖

“C言語何でも質問掲示板” へ戻る