【第1部】C言語(K&R検証)編
0025.逆ポーランド電卓を実装する
逆ポーランド電卓とは
ならば
と記述する電卓です。
K&Rでは、このサンプルの説明をするのに、複数のソースファイルへの記述が説明されています。
ここでは、それに習い、複数ファイルに記述します。
なお、以下のソースは、
レポジトリの
projects/Sample0025_1にあります。(ファイルの中身は
K&Rの説明とは若干違います)
VS2019で開けばビルドできます。
ソース紹介
もし0からプロジェクトを作成する場合は
C++コンソールアプリケーション-空のプロジェクトを作成して、以下のファイルを追加してビルドしてください。この場合
VS2019でなくても
VS2015もしくはVS2017でも実装できます。
CPPファイルに記述
calc.h
#pragma once
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cctype>
using namespace std;
#define NUMBER '0'
void push(double d);
double pop();
int getop(char s[]);
int getch();
void ungetch(int c);
stack.cpp
#include "calc.h"
#define MAXVAL 100
int sp = 0;
double val[MAXVAL];
void push(double d) {
if (sp < MAXVAL) {
val[sp++] = d;
}
else {
cout << "push: スタックがいっぱいです" << endl;
}
}
double pop() {
if (sp > 0) {
return val[--sp];
}
else {
cout << "pop: スタックが空です" << endl;
return 0.0;
}
}
getop.cpp
#include "calc.h"
int getop(char s[]) {
int i, c;
while ((s[0] = c = getch()) == ' ' || c == '\t')
;
s[1] = '\0';
if (!isdigit(c) && c != '.') {
return c;
}
i = 0;
if (isdigit(c)) {
while (isdigit(s[++i] = c = getch()))
;
}
if (c == '.') {
while (isdigit(s[++i] = c = getch()))
;
}
s[i] = '\0';
if (c != EOF) {
ungetch(c);
}
return NUMBER;
}
getch.cpp
#include "calc.h"
#define BUFSIZE 100
char buf[BUFSIZE];
int bufp = 0;
int getch() {
return (bufp > 0) ? buf[--bufp] : cin.get();
}
void ungetch(int c) {
if (bufp >= BUFSIZE) {
cout << "ungetch: これ以上追加できません" << endl;
}
else {
buf[bufp++] = c;
}
}
main.cpp
#include "calc.h"
#define MAXOP 100
int main()
{
int type;
double op2;
char s[MAXOP];
while ((type = getop(s)) != EOF) {
switch (type) {
case NUMBER:
push(atof(s));
break;
case '+':
push(pop() + pop());
break;
case '*':
push(pop() * pop());
break;
case '-':
op2 = pop();
push(pop() - op2);
break;
case '/':
op2 = pop();
if (op2 != 0.0) {
push(pop() - op2);
}
else {
cout << "0除算です" << endl;
}
break;
case '\n':
cout << '\t' << pop() << endl;
break;
default:
cout << "コマンドが不明です" << endl;
break;
}
}
return 0;
}
出力
12 34 + 56 78 - *
-1012
12.345 67.89 - 987.65 43.21 + *
-57259.1
12 34 - 56 78 + /
-156
^Z
もし浮動小数点の精度を上げたければ、
0024.小数点のある足し算を実装するで説明した方法で
coutの設定を変えてください。
サンプル説明
このサンプルは
K&Rの中でもとくに有名なサンプルです。
逆ポーランドというなじみの薄い文法ですが、矛盾なく複雑な計算もできます。
僕は、このソースで感心するのは
getop()関数の
if (c != EOF) {
ungetch(c);
}
の部分です。これは
入力を読みすぎた時に戻す処理ですが、なかなか思いつくものではないなあ、と思います。(解説になってない)
まあ、じっくり読んでください。読めば読むほど
味のあるソースです。
K&Rでの記述
元になったのは
第4章:関数とプログラム構造に記述されています。
外部変数の説明のところです。