前提・実現したいこと
プログラミングの勉強を初めて2ヶ月ほどのものです。
まだまだわからないことが多いので、お手柔らかにお願いいたします。
c言語について、通常の式を逆ポーランド記法に変換して式自体を出力するプログラムを制作しています。
main.c(関数を使って逆ポーランド記法に変換していくコード)
stack.c(スタック関連の関数を入れているコード)
stack.h (ヘッダファイル)
queue.c (キュー関連の関数を入れているコード)
queue.c(ヘッダファイル)
lib1.c(課題で提供されている関数の入った補助コード)
lib1.h(ヘッダファイル)
これらが関連のファイルで分割コンパイルして実行ファイルmain.exeを作ります。
最終的には括弧の含んだ式の処理もしたいのですが、一旦数字と符号のみの式を考えることにしました。
想定しているアルゴリズムは、
(キーボード入力から得た文字列をトークンtとする。)
(スタックの頂上の内容をsとする。)
・tが数字のとき
→tをenqueue
・tが符号のとき
→スタックが空ならtをpush
・スタックになにかあるとき
・符号優先度がs<tのとき
→tをpush
・符号優先度がs>=tのとき
→スタックが空になるかs<tになるまでpopしてその内容をenqueue
これを入力したトークンtの数だけ実行していき、最後までtを見終えたらスタックをすべてpopして、その内容をenqueueし、
queueを出力すると逆ポーランド記法になっているというものです。
発生している問題・エラーメッセージ
以下、式:1-43+2 で実行した実行結果です。
ちなみに、ほしい結果は、143-2+ です。
% ./main.exe これは最新版です input> 1-4*3+2 数字をエンキューします enqueue(1) 記号の処理を始めます push(-) 数字をエンキューします enqueue(4) 記号の処理を始めます 演算子の比較を始めます s = - 優先度は6 <ーーー(1) t = * 優先度は7 t>s push(*) 数字をエンキューします enqueue(3) 記号の処理を始めます 演算子の比較を始めます t<s s = * 優先度は7 t = + 優先度は6 pop = * enqueue(*) s = - 優先度は-1 <ーーー(2) t = + 優先度は6 push(+) 数字をエンキューします enqueue(2) --- スタックの中身出していきます --- pop = + enqueue(+) pop = - enqueue(-) 1 4 3 * 2 + - %
1行目出力は、今までの変更のバックアップとごっちゃにならないために出力してるものなので気にしないでください...()
問題の箇所は、「<ーーーー(2)」で指している部分で、優先度が正しく得ることが出来ていません。
ちなみに優先度における「-1」という出力はどの符号にも当てはまらなかった場合に出力される値です。
該当のソースコード
main.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "lib1.h" #include "stack.h" #include "queue.h" #define MAX_TOKEN_NUM 200 int main(void){ int i,j,k,kakko,result,tnum; char a,*s,m,*sp,*tp,*token[MAX_TOKEN_NUM],*p,x; printf("これは最新版です\n"); // 標準入力から1行分の文字列を読み込み,トークンに分割する. tnum = input(token,MAX_TOKEN_NUM); //■ 中置記法の式の構成要素(トークン)を先頭から順に注目しながら以下を繰り返す. for (i = 0; i < tnum; i++) { //● 今注目中のトークン t と記号スタックの状態に応じて,表 2 の通り処理する. if (isNumberStr(token[i])) { //・数字だったとき printf("数字をエンキューします\n"); enqueue(*token[i]); }else if (isOperatorStr(token[i])) { //・記号だったとき printf("記号の処理を始めます\n"); if (isStackEmpty()) { //・スタックが空だったとき push(*token[i]); }else{ //・スタックになにかあるとき printf("演算子の比較を始めます\n"); m = peek(); sp = &m; //spはpeekの値のアドレス tp = &*token[i]; //tpは注目中のトークンのアドレス //スタックの中身が演算子だったとき if (priority(tp)/*t*/ >= /*s*/priority(sp)) { printf("s = %c 優先度は%d\n",peek(),priority(sp)); printf("t = %c 優先度は%d\n",*token[i],priority(tp)); //・tのほうがsより優先度が高いとき //演算子tをpushする printf("t>s\n"); push(*token[i]); }else{ printf("t<s\n"); //・sのほうがtより優先度が高いとき while(isStackEmpty()==0){ //スタックが空になるまでpopしenqueue m = peek(); sp = &m; printf("s = %c 優先度は%d\n",peek(),priority(sp)); printf("t = %c 優先度は%d\n",*token[i],priority(tp)); if (priority(tp)/*t*/ <= /*s*/priority(sp)) { a = pop(); p = &a;//この文が何故かpriority(-1)問題において大切 enqueue(a);//ここでaの内容をenqueueしている時点で上の行の文は関係はいはず }else break; } push(*token[i]);//演算子tをpushする } } } } //stackが空になるまでenqueue printf("--- スタックの中身出していきます ---\n"); while(isStackEmpty()==0){ a = pop(); enqueue(a); } printQueue(); }
stack.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "stack.h" STACK_TYPE gStack[STACK_SIZE]; int gSNum = 0; void push(STACK_TYPE x){ gStack[gSNum++] = x; printf("push(%c)\n",x); } STACK_TYPE pop(void){ char x = gStack[gSNum - 1]; gSNum--; printf("pop = %c\n",x); return x; } void printStack(void){ int i; printf("STACK[ "); for (i=0; i<gSNum; i++){ printf("%.1lf ", gStack[i]); } printf("]\n"); } int isStackEmpty(void){ return gSNum == 0; } int isStackFull(void){ return gSNum == STACK_SIZE; } STACK_TYPE peek(void){ STACK_TYPE x; if(isStackEmpty() != 1){ //空じゃないなら x = gStack[gSNum-1]; return x; }else{ //空状態なら fprintf(stderr,"エラー:空状態でpeekはできません\n"); exit(1); } } //符号優先度を出す関数 int priority(char *op){ int result,a=-1; result = strcmp(op,"="); if(result == 0){ a = 2; } result = strcmp(op,"=="); if(result == 0){ a = 3; } result = strcmp(op,"!="); if(result == 0){ a = 3; } result = strcmp(op,"+"); if(result == 0){ a = 6; } result = strcmp(op,"-"); if(result == 0){ a = 6; } result = strcmp(op,"*"); if(result == 0){ a = 7; } result = strcmp(op,"/"); if(result == 0){ a = 7; } result = strcmp(op,"%"); if(result == 0){ a = 7; } result = strcmp(op,"^"); if(result == 0){ a = 8; } result = strcmp(op,"("); if(result == 0){ a = 99; } return a; } STACK_TYPE retrieve(POSITION n){ if(isStackEmpty()) fprintf(stderr,"retrieve:スタックが空状態です\n"); if(n<0 || n >= gSNum) fprintf(stderr,"retrieve:位置が正しくありません\n"); return gStack[n]; } int stackTop(void){ int a; a = gSNum; return a; }
queue.c
#include <stdio.h> #include <stdlib.h> #include "queue.h" QUEUE_TYPE gQueue[QUEUE_SIZE]; int gQNum = 0; void error(char *s){ fflush(stdout); fprintf(stderr,"\n%s\n",s); exit(1); } void enqueue(QUEUE_TYPE x){ if(isQueueFull()) error("enqueue:待ち行列はフル状態です\n"); gQueue[gQNum++] = x; printf("enqueue(%c)\n",x); } QUEUE_TYPE dequeue(void){ QUEUE_TYPE x; int i; if(isQueueEmpty()) error("dequeue:待ち行列は空状態です\n"); x = gQueue[0]; for(i=1;i<=gQNum-1;i++) gQueue[i-1] = gQueue[i]; gQNum--; return x; } int isQueueEmpty(void){ return gQNum == 0; } int isQueueFull(void){ return gQNum == QUEUE_SIZE; } void printQueue(void){ int i; for(i=0; i<gQNum; i++){ printf("%c",gQueue[i]); if(i != gQNum -1)printf(" "); } printf("\n"); }
かなり試行錯誤を繰り返して、宣言しているのに使っていない変数や関数がたくさんあり、
ごちゃごちゃしていて申し訳ないです。
また、lib1.cは提供されたものなので、公開できません。
試したこと
とにかくpriority関数あたりがなんだかうまく行ってないので、どのようなときに「-1」と出力されてしまうのかいろんな式で実行しましたが、どういう条件でうまく行かないのかわかりませんでした。
さらに、
main.cの一部抜粋
c
1p = &a;//この文が何故かpriority(-1)問題において大切 2enqueue(a);//ここでaの内容をenqueueしている時点で上の行の文は関係はいはず
上記、main.cのコメントでも書いたとおり、ここのp=&a;があるのと無いのとで、出力が変わってきます。
2行目コメントの通り、アルゴリズム上全く関係のない行なのですが、どういう事情なのでしょうか。
補足情報(課題について)
この課題は授業で出された課題の一部です。
教員はこれまでに習ったことを理解すれば解けるといい、質問は一切受け付けていない状況です。
しかし、問題の箇所はアドレスが関連してそうな気が素人なりに勘づいているのですが、そこは授業で習っていません。
おおよそ、スタックとキューと逆ポーランド記法について学んだと言った感じです。
逆に言うと、スタックとキューと逆ポーランド記法以外はなんとなくな理解なので、
もしかするとかなり初歩的な勘違いをしている可能性も大いにあります。
自信がないところとすれば、priority関数について引数の入れ方をpriority(sp) (sp = &m)のようなアドレスで渡すようなやり方は授業内でしたことがない。(習っていない)
授業ではこんなふうに使っていました。
int main(void){ char *op[OPNUM] = {"+", "*", "-", "/", "&", "^", "%", "=", "==", "!="}; int i; for (i=0; i<OPNUM; i++){ printf("%s -> %d\n", op[i], priority(op[i])); } return 0; }
理解度かなり低い状態ではあると思いますが、
教員に質問できない状況下でかなり時間かけて取り組みました。
回答よろしくおねがいします。
回答3件
あなたの回答
tips
プレビュー