スタックを使ったRPN計算機の計算処理

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
風鈴

スタックを使ったRPN計算機の計算処理

#1

投稿記事 by 風鈴 » 7年前

スタックを使って逆ポーランド記法の計算処理をしたいのですが、どうにも計算が上手くいかないのです。
スタック自体(istack.hとistack.c)と、入力された普通の数式(2+5*8-4/5など)をRPNに変換するものは大学の講義ページのコード例の引用で、rpn.c内の計算処理の部分を作っています。
環境はWin10でVS2017付属のコンパイラを使っています。
現在、2+5+3*5+4を計算しようとすると、出力が50になってしまいます。
計算処理のどこかが間違っているのだとは思うのですが…

istack.h

コード:

// istack.h --- int type stack interface

#include <stdbool.h>

struct istack;

typedef struct istack *istackp;
istackp istack_new(int size); // allocate new stack

bool istack_isempty(istackp p); // test if the stack is empty

void istack_push(istackp p, int v); // push a value

int istack_pop(istackp p);      // pop a value and return it

int istack_top(istackp p);      // peek the topmost value


istack.c

コード:

// istack.c --- int type stack impl. with array
#include <stdlib.h>

#include "istack.h"
struct istack { int ptr; int *arr; };

istackp istack_new(int size) {

  istackp p = (istackp)malloc(sizeof(struct istack));

  p->ptr = 0; p->arr = (int*)malloc(size * sizeof(int));

  return p;

}

bool istack_isempty(istackp p) { return p->ptr <= 0; }

void istack_push(istackp p, int v) { p->arr[p->ptr++] = v; }

int istack_pop(istackp p) { return p->arr[--(p->ptr)]; }

int istack_top(istackp p) { return p->arr[p->ptr - 1]; }

rpn.c

コード:

//RPN
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include "istack.c"

int operprec(int c) {
  switch(c) {
    case '+': case '-': return 1;
    case '*': case '/': case '%': return 2;
    case '^': return 3;
    default: return 0;
  }
}
int main(void) {
  istackp s = istack_new(200);
  istackp t = istack_new(200);
  int c, d;
  printf("s> ");
  for(c = getchar(); c != '\n' && c != EOF; c = getchar()) {
    if(isdigit(c)) { 
        istack_push(t, c);
        putchar(c);
        continue;
    }
    while(!istack_isempty(s) && operprec(istack_top(s)) >= operprec(c)) {
      d = istack_pop(s);
      istack_push(t ,d);
      putchar(d);
    }
    istack_push(s, c);
  }
  while(!istack_isempty(s)) { 
      d = istack_pop(s);
      istack_push(t ,d);
      putchar(d);
  }
  //ここから計算のプログラム
  int num, tmp;
  while(!istack_isempty(t)) {
      num = istack_pop(t);
      if(isdigit(num)) {
          istack_push(s, (int)num);
          continue;
      } else {
          switch(num) {
              case '+':
                    istack_push(s, istack_pop(s)+istack_pop(s));
                    break;
              case '*':
                    istack_push(s, istack_pop(s)*istack_pop(s));
                    break;
              case '-':
                    tmp = istack_pop(s);
                    istack_push(s, istack_pop(s)-tmp);
                    break;
              case '/':
                    tmp = istack_pop(s);
                    istack_push(s, istack_pop(s)/tmp);
                    break;
              case '%':
                    tmp = istack_pop(s);
                    istack_push(s, istack_pop(s)%tmp);
                    break;
              case '^':
                    tmp = istack_pop(s);
                    istack_push(s, istack_pop(s)^tmp);
                    break;
          }
      }
  }
  /*while(!istack_isempty(s)) { 
      putchar(istack_pop(s));
  }*/
  printf(" = %d", istack_pop(s));
  putchar('\n');
  return 0;
}
出力が
s> 2+5+3*5+4
25+35*+4+ = 50
のようになってしまいます。

間違いの箇所と理由などを教えていただけたらと思います。
よろしくお願いします。

かずま

Re: スタックを使ったRPN計算機の計算処理

#2

投稿記事 by かずま » 7年前

間違いの箇所
41、42行目: スタック t には、逆ポーランド記法に変換された式が
 入っているが、それを後ろから順番に取り出して計算に使っている。
44行目: 数字を数値に変換せずにスタック s にプッシュしている。
68行目: ^ はべき乗演算子のはずなのに、排他的論理和演算をしている。

修正方法

コード:

41: for (int i = i < t->ptr; i++) {
42:     num = t->arr[i];
44:         istack_push(s, num - '0');
68:                 istack_push(s, pow(istack_pop(s), tmp));
struct istack のメンバである ptr や arr を直接参照してはいけない場合は、
41、42行目はそのままにして、直前にスタック t を逆順にする
次のコードを追加する。

コード:

  while (!istack_isempty(t)) istack_push(s, istack_pop(t));
  istackp p = s; s = t; t = p;

かずま

Re: スタックを使ったRPN計算機の計算処理

#3

投稿記事 by かずま » 7年前

かずま さんが書きました:
7年前

コード:

41: for (int i = i < t->ptr; i++) {
訂正。

コード:

41: for (int i = 0; i < t->ptr; i++) {
ところで、数字は1桁でいいんですか?

風鈴

Re: スタックを使ったRPN計算機の計算処理

#4

投稿記事 by 風鈴 » 7年前

添削ありがとうございます。
間違っていた箇所とその原因に納得がいきました。

数字に関しては入力は1桁で構わないということでした。

風鈴

Re: スタックを使ったRPN計算機の計算処理

#5

投稿記事 by 風鈴 » 7年前

今回はスタックの基礎を扱う演習のため、なるべく優しくしたのではないかと思います。

かずま

Re: スタックを使ったRPN計算機の計算処理

#6

投稿記事 by かずま » 7年前

2^3^2 が (2^3)^2 と解釈されて 23^2^ = 64 となりますが、
本来は、2^(3^2) と解釈して 232^^ = 512 とするものだと
私は思います。

232 = 29 = 512

風鈴

Re: スタックを使ったRPN計算機の計算処理

#7

投稿記事 by 風鈴 » 7年前

確かにそうですね…
アドバイスありがとうございます。
少し考えてみます。

返信

“C言語何でも質問掲示板” へ戻る