質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

1回答

871閲覧

逆ポーランド記法電卓

kakakaaka

総合スコア17

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2017/11/12 11:28

改善点はありますか?

#include <stdio.h> #include <string.h> #define MAX 100 int stack[MAX]; //stackは配列を利用して実現 int top = 0; //stackは最上部(データ実際に格納する場所) int push(int x); int pop(); //配列を利移用して、スタックデータ構造を実現する // push : データの格納 int push(int x){ if(top == MAX) return -1; //満杯なら-1をエラーとして返す stack[top] = x; //データの格納 top++; //次に格納する位置へtopを移動(1つ増やす) return 1; } //pop : データ取り出し int pop(){ --top; //現在データが格納されているところにtopを移す return stack[top]; //最上部のデータを取り出して返す } //operator : 演算子 int operator(mozi){ int a,b; switch(mozi){ case '+': a=pop(); b=pop(); push(a+b); break; case '-': a=pop(); b=pop(); push(a-b); break; case '*': a=pop(); b=pop(); push(a*b); break; case '/': a=pop(); b=pop(); push(a/b); break; } } int main(){ char *p_push = "push"; char *p_pop = "pop"; char *p_operator = "operator"; char input[10]; int su,mozi; for(int i=0; i<10; i++){ printf("Input operation: push/pop/operator:"); scanf("%s", input); if(strcmp(input, p_push) == 0){ printf("Input data : "); scanf("%d", &su); push(su); }else if(strcmp(input, p_pop) == 0){ printf("popped data : %d\n",pop(su)); }else if(strcmp(input, p_operator) == 0){ printf("Input data : "); scanf("%s", &mozi); operator(mozi); }else{ printf("Stack is empty! "); break; } } }

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

改善点はありますか?

質問が漠然としているので、一般的なコードの書き方だけ回答します。
実装が正しく動くかどうか、といったことは自力でテストして、
詰まったときに、具体的にまた質問してください。


さて、コードを見た第一印象ですが、
前半部はスッキリしてるのに、
後半部のmain関数、forループの部分が
ややゴチャゴチャしてます。

まあ電卓ならこれくらい大した問題ではないでしょうが、
もっと本格的な言語処理系の作成に進むときに、
分かりにくく、直しにくくなっていきます。

とくにC言語は手続き型言語なので、
深い入れ子が分かりにくくなりやすい原因です。
入れ子の数が五重、十重に増えたところをイメージしてください。

何重になったらリファクタリングするのか、意見は分かれますが、
少なくとも、際限なく入れ子が増えたら
理解不可能になるのは異論ないと思います。

またコードが小さいので、ずいぶん気が早いと感じられるかもしれませんが、
コードが大きくなった後で、壊さないように直すのは非常に大変なのです。
現実的には「直すより、一から書き直した方が早い」になりがちです。


c

1if(a == 0){ 2 func_a(); 3}else if(b == 0){ 4 func_b(); 5}else if(c == 0){ 6 func_c(); 7}else{ 8 func_d(); 9}

では、具体的にどうすればいいのか。
上の例のように下請けの関数に切り出していくのが、
単純で分かりやすいと思います。

それから、else ifはインデントを掘り進まないで、
同じ高さにそろえてある方が読みやすいと思います。

さらに判定部分を関数に切り出して、
returnと組み合わせると、elseは消せます。
分岐が排他的でなくなった場合にとくに有効です。

c

1func_select(){ 2 if(a == 0){ 3 func_a(); 4 return; 5 } 6 if(b == 0){ 7 func_b(); 8 return; 9 } 10}

このほか、関数を呼ぶ代わりに、変数にフラグを立てて、
後でスイッチ文で切り分ける手法もあります。
これは、1回関数を呼ぶだけではなく、複数回呼ぶときに、
フラグを再利用できる場合にはスッキリします。

行数が増えるのが気になるかもしれませんが、
分かりにくくてデバッグで詰まる時間の方が、
貴重だと考えています。

小さい関数に切り出していくと、個別にテストしやすくなります。
とくに本格的な言語処理系は複雑で難解になりやすいので、
部品化が有効になるジャンルだと思います。


最後に、電卓から先の、発展的な話題を言いますと、
本格的な言語処理系では、たとえば再帰下降構文解析(LL(1))では、
言語仕様のBNFに合わせて、関数を書きくだすと分かりやすい。

また、オブジェクト指向で書くならビジターパターンで、
走査と処理の責務を分けたりしますし、
関数型のパーサコンビネータなどは、部品化の極地です。

そもそも言語処理系は、言語を単語に分解して、
解読していく仕組みなので、処理を分解して、
単語などの言語の単位と処理の単位を一致させると、理解しやすくなるのです。

投稿2017/11/12 13:03

LLman

総合スコア5592

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kakakaaka

2017/11/12 13:09

ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問