C言語関係掲示板

過去ログ

No677 アンドゥ付きの電卓

[戻る] [ホームページ]
No.7703

簡単な(はずだった)アンドゥの作り方
投稿者---ド初心者(2003/06/23 18:24:33)


すいません。また質問します。
どなたか、教えてください。よろしくお願いします。

簡単なスタックを使った電卓を作成しろという問題なんですけど
その機能の1つにアンドゥがあるんですが・・・
ABCD→ABC→ABCDにするというものなんです。
ちなみにソースです。

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

#define STACK_MAX  100        /* スタックサイズ */
/* 関数のプロトタイプ宣言 */
void     push(double);       /* スタックにデータを積む関数 */
double   pop(void);          /* スタックからデータを取り出す関数 */
void     show_stack(void);   /* スタックの内容をすべて表示する関数 */
double   kaijo(double);		 /*階乗計算用関数*/
void	 swap(double *,double *);
void show_stack2(void);
/* グローバル変数 */
double stack[STACK_MAX];     /* スタック領域*/
double stk_ud[STACK_MAX];    /*アンドゥ用スタック*/
int     sp = 0,sp2 = 0;      /* スタックポインタ */

int main(void)
{
	char	data[STACK_MAX];
    double   n,x,y,i=0;

	printf("+-----STACK-----\n");
	printf("+---------------\n>");

	do{
		scanf("%s",data);

		if(atof(data)!=0.0){
			push(atof(data));
			x = pop();stk_ud[sp2] = x;
			push(x);
			show_stack();
		}else{
			switch(data[0]){
			case 'p':
				n = pop();stk_ud[sp2] = n;
				show_stack();
				break;
			case '+':
				if(sp>=1){
					x = pop();stk_ud[sp2] = x;
					y = pop();stk_ud[sp2] = y;
					push(x + y);
					show_stack();
				}else{
					printf("スタックが足りません\n");
				}
				break;
			case '-':
				if(sp>=1){
					x = pop();stk_ud[sp2] = x;
					y = pop();stk_ud[sp2] = y;
					push(x - y);
					show_stack();
				}else{
					printf("スタックが足りません\n");
				}
				break;
			case '*':
				if(sp>=1){
					x = pop();stk_ud[sp2] = x;
					y = pop();stk_ud[sp2] = y;
					push(x * y);
					show_stack();
				}else{
					printf("スタックが足りません\n");
				}
				break;
			case '/':
				if(sp>=1){
					x = pop();stk_ud[sp2] = x;
					y = pop();stk_ud[sp2] = y;
					push(x / y);
					show_stack();
				}else{
					printf("スタックが足りません\n");
				}
				break;
			case 'm':
				x = pop();stk_ud[sp2] = x;
				push(-(x));
				show_stack();
				break;
			case 'g':
				x = pop();stk_ud[sp2] = x;
				push(1 / x);
				show_stack();
				break;
			case 's':
				while(sp!=0){
					x = pop();stk_ud[sp2] = x;
					if(sp==0){
						push(x);
						break;
					}else{
						y = pop();stk_ud[sp2] = y;
					}
					push(x + y);
				}
				show_stack();
				break;
			case 'a':
				while(sp!=0){
					x = pop();stk_ud[sp2] = x;
					i++;
					if(sp==0){
						push(x);
						break;
					}else{
						y = pop();stk_ud[sp2] = y;
					}
					push(x + y);
				}
				x = pop();stk_ud[sp2] = x;
				push(x/i);
				show_stack();
				break;
			case 'i':
				x = pop();stk_ud[sp2] = x;
				push((int)x);
				show_stack();
				break;
			case '!':
				x = pop();stk_ud[sp2] = x;
				x = kaijo(x);
				push(x);
				show_stack();
				break;
			case '^':
				x = pop();stk_ud[sp2] = x;
				y = pop();stk_ud[sp2] = y;
				n = 1;
				for(i=0;i<y;i++){
					n *= x;
				}
				push(n);
				show_stack();
				break;
			case 'c':
				while(sp != 0){
					x = pop();stk_ud[sp2] = x;
				}
				show_stack();
				break;
			case 'u':
				swap(stack,stk_ud);
				show_stack2();
				break;
			case 'h':
				printf("ヘルプ表示 コマンド一覧表\n");
				printf("p スタックから取り出す。 + 足し算 - 引き算\n");
				printf("* 掛け算 / 割り算 m 符号変換 g 逆数\n");
				printf("s 合計 a 平均 i 整数化 ! 階乗計算 ^ べき乗\n");
				printf("c スタッククリア u アンドゥ q 終了\n");
				break;
			case 'q':
				printf("終了します\n");
				return 0;
			default :
				printf("入力ミス\n");
				break;
			}
		}
  }while(data[0] != 'q');
	return 0;
}
void push(double d)
{
    if(sp >= STACK_MAX)  /* スタックポインタが最大 */
        return;         
    stack[sp] = d;       /* スタックにデータ格納 */
    sp++;          /* スタックポインタに1加算 */
}
double pop(void)
{
    sp--;sp2++;  /* スタックポインタから1減算 */
    return stack[sp];   /* スタックのデータを返す */
}
double kaijo(double d)
{
	return ((d == 0) ? 1: d * kaijo(d - 1));
}
void	 swap(double *x,double *y){
	double * tmp;

	tmp = x;
	x = y;
	y = tmp;

}
void show_stack(void)
{
    int     i;

	printf("+-----STACK-----\n");
	for(i=0;i<sp;i++){
		printf("%f\n",stack[i]);
	}
	
	printf("+---------------\n>");
}
void show_stack2(void)
{
    int     i;

	printf("+-----STACK-----\n");
	for(i=0;i<sp2;i++){
		printf("%f\n",stack[i]);
	}
	
	printf("+---------------\n>");
}


No.7708

Re:簡単な(はずだった)アンドゥの作り方
投稿者---もぐりん(2003/06/23 19:47:33)


で、ソースをみてどうしてもらいたいのでしょうか?
開発環境は?
OSは?
どうしてもらいたいのかを詳細に書いてください。
(ソースのおかしいところを教えてください、とか。
コンパイル通ったけど動作がおかしい、とか。)


No.7712

Re:簡単な(はずだった)アンドゥの作り方
投稿者---あかま(2003/06/23 21:44:34)


軽く読んでみたのだけれどいまいち何がしたいのかわからないです。
こういう物の定番、逆ポーランド記法とは違うのですか?
とりあえず、入力方法などを書いてくれると嬉しいです。

あとこれ。
if(atof(data)!=0.0){
			push(atof(data));
			x = pop();stk_ud[sp2] = x;
			push(x);
			show_stack();
		}

スタックにpushした後、速攻でpopとか。
アンドゥのstk_udの使い方がわからないです。
どのような方法でアンドゥを実現しようとしているのでしょうか?

No.7725

説明不足だったようで・・・
投稿者---ド初心者(2003/06/24 18:22:36)


ようはスタックで電卓を作るで、
四則演算だけじゃなくいろいろ機能をつけようということです。

普通の電卓だけならスタック1つですむのですが、
アンドゥをする場合(1つ前の行動にもどす作業)の時
スタックで入力した計算する前のデータを保存したいのです。

だから、無理やりpopしてstk_udに格納しているのですが
u(アンドゥ)を押したときswapで交換して前のデータを表示しようとしたの
ですが、どうもうまくいきません。
何も変わらなかったり、ゴミデータが入っていたりと・・・・

開発環境はとくに関係無いと思って書いておりましたが(このソースだと
どのコンパイラでも出来ると思いまして)書いておきます。

OS,XP;VC++です。
よろしくお願いします。いろいろすいませんでした。

No.7726

Re:説明不足だったようで・・・
投稿者---あかま(2003/06/24 19:00:04)


まだイマイチ仕様がつかめてなかったり。
普通の電卓ならスタック自体いらないですし。
とりあえず問題は下のどこかにあるわけですが、
呼び出し
swap(stack,stk_ud);

関数
void swap(double *x,double *y){
	double * tmp;

	tmp = x;
	x = y;
	y = tmp;

}

これは配列の先頭ポインタを入れ替えてしまおうというものですか?
コンパイラの仕様にもよると思いますが、あまり良くないかと思います。

double *stack_p,*stk_ud_p;
stack_p = stack;
stk_ud_p = stk_ud

として、呼び出し時には
swap(stack_p,stk_ud_p);
こんな感じにするのがよいかと


No.7727

う〜ん・・・・
投稿者---ド初心者(2003/06/24 19:36:17)


レスありがとうございます。

ご指摘のところ直してみました。
stack_pのところらへんを直してみましたが変化はありませんでした。

いまいち仕様がわからないということなのですが、
簡単に言うとスタックの勉強です。
で、スタックの保存用のアンドゥ用スタックがもうひとつ必要なんですが・・・

stk_udスタックに保存するタイミングがずれていると思って
いるのですが、どこかわかりません。

計算後のスタックまで保存してしまって本末転倒になっています。
なにかヒントみたいなものはないでしょうか?う〜ん・・・・

No.7730

Re:う〜ん・・・・
投稿者---あかま(2003/06/24 21:14:37)


>stk_udスタックに保存するタイミング
これが一番重要でしょうねぇ。とりあえず、アンドゥがどこまでやってくれるのかをはっきりさせるべきでしょう。
現状態ではなんでもかんでも保存させているようなのですが。

次のような入力をしたときはどのような動作になるのですか?
1.3回連続で数字を入力するとスタックに3つ溜まる。アンドゥ用のスタックにも同様に溜まる。
2."+"を入力するとスタックから2つpopして、アンドゥ用スタックにそれを2つpopする。計算結果をスタックにpopする。

プログラムではこんな動作をしていると思います。
さて、ここで"u"を入力してアンドゥを使うとどうなる予定なのでしょうか?

ここで、スタックを計算前に戻すには、
1.最後のスタック(計算結果)を削除する。
2.アンドゥから2つpopする。
3.2つめのpopをスタックにpush、1つめのpopをスタックにpush
この動作で元に戻るかと思います。

しかし、関数swapではこのような動作をしていません。
ド初心者さんの考えているアンドゥはこれとは違うのですか?
恐らく、配列1つでアンドゥを実現するにはこのやり方ぐらいしかないと思うのですが。

そしてさらに、"u"を入力した時のことを考えてみましょう。
このときは、”1回目のアンドゥ前に戻す”と”数字を2つまで入力した状態にに戻す”の2通りがあると思うのですが、どちらでしょう?
アプリケーションによってどちらもあったりするので、私はどちらが正しいのかわかりません。
便利なのは後者の方ですが。

No.7742

Re:う〜ん・・・・
投稿者---あかま(2003/06/25 00:21:37)


間違えた。
>ここで、スタックを計算前に戻すには、
>1.最後のスタック(計算結果)を削除する。
>2.アンドゥから2つpopする。
>3.2つめのpopをスタックにpush、1つめのpopをスタックにpush
3.は普通にpopした順にpushすればいいですね。

それと気になることが。
double pop(void)
{
    sp--;sp2++;  /* スタックポインタから1減算 */
    return stack[sp];   /* スタックのデータを返す */
}

x = pop();stk_ud[sp2] = x;

スタック用のpop関数でアンドゥのカウンタを動かしていますが、
こういう書き方をするとプログラムが思い通りに動かない原因になります
(動きが追いにくくなる。stk_udのみpush、popしたくなったときにやりにくい等)。
stk_ud用のpush、pop関数を作ったほうがいいかと思います。

No.7757

Re:簡単な(はずだった)アンドゥの作り方
投稿者---nop(2003/06/25 10:15:29)


意図している事はよくわからないが、
swap() の処理は明らかに間違っているかと。

内部変数の中身を入れ替えても実態は入れ替わらない訳で…。
以下の様にすれば swap() 関数は正しく動作するかと。

void swap( double **a, double **b )
{
    double *tmp;

    tmp = *a;
    *a = *b;
    *b = tmp;
}


No.7785

え〜と・・(汗)
投稿者---ド初心者(2003/06/25 22:47:32)


レスありがとうございます。

swap(double **x,double **y)・・・・・と
やってみましたが、エラーが出ました。

おそらく、あかま様の指導のもとコードを改良していましたので
それでかと。

改良したのは、stk_udスタック用のpop2,push2を作りました。
今はどこでsp2を増減したらよいか考え中です。

最初に書いたと思うんですが(汗)
僕の作りたいアンドゥは、行ったり来たり、前 後 前 後

例としては、2,3,4でスタックが入ってるとして'+'を押すと
スタックに、2,7。
'u'を押すと
スタックに、2,3,4
もう一度'u'を押すと
スタックに、2,7にしたいのです。

ご指摘どうもありがとうございました。またアドバイスよろしくお願いします。

No.7788

Re:え〜と・・(汗)
投稿者---nop(2003/06/25 23:44:05)


>swap(double **x,double **y)・・・・・と
>やってみましたが、エラーが出ました。

ちなみに、呼び出し元である

>swap(stack,stk_ud);

は修正しましたか?
関数の定義が変われば、
プロトタイプやその関数の呼び出しも修正が必要になります。
呼び元を以下の様に修正しましょう。

swap( &stack, &stk_ud );

No.7789

Re:え〜と・・(汗)
投稿者---かずま(2003/06/25 23:45:54)


> 例としては、2,3,4でスタックが入ってるとして'+'を押すと
> スタックに、2,7。
> 'u'を押すと
> スタックに、2,3,4
> もう一度'u'を押すと
> スタックに、2,7にしたいのです。

こんなことがしたいのですか。
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

double fact(double d) { return (d == 0) ? 1 : d * fact(d - 1); }

/* stack interface */
void   push(double);
double pop(void);
int    empty(void);
void   swap_stack(void);
void   save_stack(void);
void   show_stack(void);
void   clear_stack(void);

int main(void)
{
    char data[256];  double x, i;

    show_stack();
    while (scanf("%255s", data) == 1) {
        if (sscanf(data, "%lf", &x) == 1) {
            save_stack(); push(x); show_stack();
            continue;
        }
        switch (data[0]) {
        case 'q':
            printf("終了します\n"); return 0;
        case 'u':
            swap_stack(); show_stack(); break;
        case 'h':
            printf("ヘルプ表示 コマンド一覧表\n");
            printf("p スタックから取り出す。 + 足し算 - 引き算\n");
            printf("* 掛け算 / 割り算 m 符号変換 g 逆数\n");
            printf("s 合計 a 平均 i 整数化 ! 階乗計算 ^ べき乗\n");
            printf("c スタッククリア u アンドゥ q 終了\n");
            break;
        default:
            save_stack();
            switch (data[0]) {
            case 'p': pop(); break;
            case '+': push(pop() + pop()); break;
            case '-': x = pop(); push(pop() - x); break;
            case '*': push(pop() * pop()); break;
            case '/': x = pop(); push(pop() / x); break;
            case 'm': push(-pop()); break;
            case 'g': push(1/pop()); break;
            case 's': x = 0; while (!empty()) x += pop(); push(x); break;
            case 'a': 
                for (x = i = 0; !empty(); i++) x += pop();
                push(i ? x / i : 0); break;
            case 'i': push((int)pop()); break;
            case '!': push(fact(pop())); break;
            case '^': x = pop(); push(pow(pop(), x)); break;
            case 'c': clear_stack(); break;
            default: printf("入力ミス\n"); continue;
            }
            show_stack();
            break;
        }
    }
    return 0;
}

/* stack implementation */

#define STACK_MAX  100

double s1[STACK_MAX];
double s2[STACK_MAX];
double *stack = s1;
double *undo_stack = s2;
int sp = 0;
int sp2 = 0;

void push(double d)
{
    if (sp < STACK_MAX)
        stack[sp++] = d;
    else
        puts("stack full");
}

double pop(void)
{
    if (sp > 0) return stack[--sp];
    puts("stack empty");
    return 0;
}

int empty(void)
{
    return sp == 0;
}

void swap_stack(void)
{
    double *t; int i;
    t = stack, stack = undo_stack, undo_stack = t;
    i = sp, sp = sp2, sp2 = i;
}

void save_stack(void)
{
    int i;
    for (i = 0; i < sp; i++)
        undo_stack[i] = stack[i];
    sp2 = sp;
}

void clear_stack(void)
{
    sp = 0;
}

void show_stack(void)
{
    int i;
    printf("+-----STACK-----\n");
    for (i = 0; i < sp; i++)
        printf("   %.15g\n", stack[i]);
    printf("+---------------\n>");
}

題名に感想を書かないように。

No.7807

お礼状
投稿者---ド初心者(2003/06/26 17:52:14)


あかま様、nop様、かずま様どうもありがとうございました。

あかま様、ご親切にご指導ありがとうございました。

nop様、・・・実はというと**(ポインタのポインタ?)は
よく知らなくて・・・もうしわけないです。勉強します。すいませんでした。

かずま様、すごいコードです。完璧です。まだ、全部コード理解したわけじゃ
ないですが、必ず理解しものにしたいです。ありがとうございました。