K&R 演習4-4 解答 (プログラミング言語C 第2版)

スポンサーリンク

鍛錬 825

K&R 演習4-4 解答 (プログラミング言語C 第2版)

ポップすることなくスタックの一番上の要素を印刷し,それを複製し,また上の二つの要素を交換するコマンドを追加せよ。

B.W.カーニハン D.M.リッチー 石田晴久 訳 『プログラミング言語C 第2版 ANSI 規格準拠』, (共立出版, 2017), pp.96.

スポンサーリンク

プログラム

本書中の逆ポーランド記法を使用した電卓プログラムに、次の機能を付加したプログラム、kr_4_4.c です。

機能 コマンド
スタックにおける一番上の値をポップせずに印字し、複製する機能 d
スタックにおける上2つの要素を交換する機能 c

以下の機能は、演習 4-3 で追加済みです。

機能 コマンド
モジュロ(%)(剰余演算)計算機能 %
マイナス(-)演算子である単項が含まれる場合の計算機能 無し

pp.96 の時点でまだ登場していないライブラリ関数などはできる限り使用しないようにし、本書中で作成した関数を使用するようにしています。
記述方法(「 char *s 」または「 char s[] 」など)についても、できる限り pp.96 時点での記載通りにしています。

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

// preprocessor
#define MAXOP   100		// 被演算数,演算子の最大サイズ
#define NUMBER  '0'		// 数字が存在した
#define MAXVAL  100		// スタックの最大深さ
#define BUFSIZE 100		// 入力文字を押し戻す際に利用するバッファのサイズ

// prototype
int getop(char[]);
void push(double);
double pop(void);
int getch(void);
void ungetch(int);
void NoPopPrintAndDup(void);
void ChangeTwoElmOnStack(void);

// variable
int sp = 0;				// スタックポインタ
double val[MAXVAL];		// スタック
char buf[BUFSIZE];		// ungetch()用のバッファ
int bufp = 0;			// バッファbufにおける次の空き位置

// main
int main(void)
{
	int type;
	double op1, 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 {
					printf("error: zero divisor\n");
				}
				break;
			case '%':
				op2 = pop();
				op1 = pop();
				if (op2 != 0.0) {
					push(op1 - (op2 * (int)(op1 / op2)));
				}
				else {
					printf("error: zero modulo\n");
				}
				break;
			case 'd':
				NoPopPrintAndDup();
				break;
			case 'c':
				ChangeTwoElmOnStack();
				break;
			case '\n':
				printf("\t%.8g\n", pop());
				break;
			default:
				printf("error: unknown command %s\n", s);
				break;
		}
	}
	
	return 0;
}

// =======================================================
// @brief  スタックにおける一番上の値をポップせずに印字し,
// @brief  複製する.
// @param  無し
// @return 無し
// @note   無し
// =======================================================
void NoPopPrintAndDup(void)
{
	int c;
	
	if (sp > 0) {
		printf("\t%.8g\n", val[sp - 1]);
	}
	else {
		printf("error: stack empty\n");
	}
	
	while ((c = getch()) != '\n' || c == EOF) {
		;
	}
}

// =============================================
// @brief  スタックにおける上2つの要素を交換する
// @param  無し
// @return 無し
// @note   無し
// =============================================
void ChangeTwoElmOnStack(void)
{
	double d_tmp1, d_tmp2;
	
	if (sp >= 2) {
		d_tmp2 = pop();
		d_tmp1 = pop();
		push(d_tmp2);
		push(d_tmp1);
	}
	else {
		printf("error: stack empty\n");
	}
}

// =====================================
// @brief  fの値をスタックにプッシュする
// @param  f [in],プッシュする値
// @return 無し
// @note   無し
// =====================================
void push(double f)
{
	if (sp < MAXVAL) {
		val[sp++] = f;
	}
	else {
		printf("error: stack full, can't push %g\n", f);
	}
}

// ==============================================
// @brief  スタックから一番上の値をポップして返す
// @param  無し
// @return val[--sp] -> スタックから取得した値
// @note   エラーの場合は return で 0.0 を返す
// ==============================================
double pop(void)
{
	if (sp > 0) {
		return val[--sp];
	}
	else {
		printf("error: stack empty\n");
		return 0.0;
	}
}

// ==================================================
// @brief  次の演算子あるいは数値の被演算数を取得する
// @param  s [out],取得した文字列
// @return c -> 演算子や改行コード
// @return NUMBER -> フラグ(数値を取得した)
// @note   無し
// ==================================================
int getop(char s[])
{
	int i, c;
	
	while ((s[0] = c = getch()) == ' ' || c == '\t') {
		;
	}
	s[1] = '\0';
	if (!isdigit(c) && c != '.' && c != '-') {
		return c;
	}
	
	i = 0;
	if (c == '-') {
		while (isdigit(s[++i] = c = getch())) {
			;
		}
		if (i == 1) {
			if (c != EOF) {
				ungetch(c);
			}
			c = '-';
			return c;
		}
	}
	else 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;
}

// =================================================
// @brief  1文字を取得する
// @param  無し
// @return buf[--bufp] -> バッファに押し戻された文字
// @return getchar() -> 入力された1文字
// @note   無し
// =================================================
int getch(void)
{
	return (bufp > 0) ? buf[--bufp] : getchar();
}

// ============================================
// @brief  文字を入力に戻す(バッファに格納する)
// @param  c [in],入力に戻す文字
// @return 無し
// @note   無し
// ============================================
void ungetch(int c)
{
	if (bufp >= BUFSIZE) {
		printf("ungetch: too many characters\n");
	}
	else {
		buf[bufp++] = c;
	}
}
スポンサーリンク

実行結果

以下は、プログラム kr_4_4.c を実行しています。

***@ubuntu:~/***/test/c$ 
***@ubuntu:~/***/test/c$ gcc -Wall -Wextra kr_4_4.c -o kr_4_4
***@ubuntu:~/***/test/c$ ./kr_4_4
1 2 + d
	3
10 *
	30
-1 5 -
	-6
-1 5 c -
	6
タイトルとURLをコピーしました