逆ポーランド記法の電卓

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

逆ポーランド記法の電卓

#1

投稿記事 by cookierinon » 11年前

C言語初心者です。プログラミング言語C 第2版(K&R)の内容について質問させていただきます。 今回の内容は質問していいようなものなのか、と躊躇したのですが…どうしてもわかりませんでした。
P92~96の逆ポーランド電卓プログラムです。スタックにプッシュ、ポップするやつです。
「12-45+*」でトレースしようとするのですが、まずスタックに格納される値が「12.0」になってしまうのです。
以下、コードを部分的に書き出してトレースの様子を記述します。

コード:

/* 逆ポーランド電卓プログラム */
main()
{
    int type;
    double op2; /* -と÷の被演算数を区別するためのもの */
    char s[MAXOP]; /* MAXOP == 100 */

    while((type = getop(s)) != EOF){
        switch (type){
        case NUMBER:
            push(atof(s));	// atof()は文字列sをdouble型に変換して返します
            break;
/* 以下省略。わからないのはgetop関数内の挙動とpush(atof(s));の部分ですので。 */
getop関数です。

コード:

/* getop: 次の演算子あるいは数値の被演算数をとってくる */
int getop(char s[])
{
    int i, c;

    while ((s[0] = c = getch()) == ' ' || c == 't')
        ;
    s[1] = '\0';
    if ( ! isdigit(c) && c != '.')	// isdigit()は '0' <= c && c <= '9' の真偽を返す関数です
        return c;
    i = 0;
    if (isdigit(c)) /* 整数部を集める */
        while(isdigit(s[++i] = c = getch()))
            ;
// getch()は不要な文字を読み戻す関数で、記述しませんがchar buf[]に演算子が格納されていなければgetchar()を返します
    if (c == '.') /* 小数部を集める */
        while (isdigit(s[++i] = c = getch()))
            ;
    s[i] = '\0';
    if (c != EOF)
        ungetch(c);
// ungetch()は文字を入力に戻します。getch()に演算子が渡されるとbuf[]に格納され、ungetch()がそれを返します
    return NUMBER; /* NUMBER == '0' です。main関数に数が集められたという目印を返します
}
「12-45+*」でトレースするとこのgetop関数でs[]に'1''2''\0'が格納され、'-'がbuf[]に格納されるはずです。
そしてmain()のswitch内でatof(s)が12.0を返すのです。何故?
以下、atof(s)の中身を記述します。P87にものっています。

コード:

#include <stype.h>

/* atof: 文字列sをdoubleに変換する */
double atof(char s[])
{
    double val, power;
    int i, sign;

    for(i = 0; isspace(s[i]); i++)	/* 空白を飛ばす */
        ;
    sign = (s[i] == '-') ? -1 : 1;
    if (s[i] == '+' || s[i] == '-')
        i++;
    for (val = 0.0; isdigit(s[i]); i++)
        val = 10.0 * val + (s[i] - '0');
    if (s[i] == '.')
        i++;
    for (power = 1.0; isdigit(s[i]); i++){
        val = 10.0 * val + (s[i] - '0');
        power *= 10.0;
    }
    return sign * val / power;
}
繰り返しますが、わからないのはgetop関数内の挙動とpush(atof(s));の部分です。どちらかを誤って解釈しているのだと思うのですが…

box
記事: 2002
登録日時: 14年前

Re: 逆ポーランド記法の電卓

#2

投稿記事 by box » 11年前

cookierinon さんが書きました: 「12-45+*」でトレースしようとするのですが、まずスタックに格納される値が「12.0」になってしまうのです。
1
から
2
を引いた結果に
4

5
を足した結果をかける、
という計算をしたいのであれば、
1 2 - 4 5 + *
という風に、少なくとも数値どうしの間には空白が必要ではなかろうか、と思います。
12
と入力すると12.0とみなすのは必然であるような気がします。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

cookierinon

Re: 逆ポーランド記法の電卓

#3

投稿記事 by cookierinon » 11年前

自分自身些細なところを見落としているんだろうな、と思っていたのですが、空白(スペース)でしたか…
どうしても気づけませんでした、boxさん、ありがとうございました。

そこで新たに疑問がわいたのですが、入力において空白を使った経験といえば文字入力の際の単語の間に挿入したくらいで
数字の入力において空白を入れるというのがいまいちピンときません、逆ポーランド記法関連の入力ではオペランドの間全てに空白を挟めばよいのでしょうか?

box
記事: 2002
登録日時: 14年前

Re: 逆ポーランド記法の電卓

#4

投稿記事 by box » 11年前

cookierinon さんが書きました: 逆ポーランド記法関連の入力ではオペランドの間全てに空白を挟めばよいのでしょうか?
くだんのコードにおいてはそうである、というだけのことです。
もしかしたら、別のコードでは、
1,2,-,4,5,+,*
のようにカンマで区切る必要があるかもしれません。当該のプログラムの仕様によります。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

アバター
へにっくす
記事: 634
登録日時: 13年前
住所: 東京都

Re: 逆ポーランド記法の電卓

#5

投稿記事 by へにっくす » 11年前

cookierinon さんが書きました:逆ポーランド記法関連の入力ではオペランドの間全てに空白を挟めばよいのでしょうか?
単語だって空白を入れずに続けて書いたら単語じゃなくなりますよね。
数字だって、数字の羅列を上げるのには空白など区切りを入れる必要があります。1と2、2つあげたいのに12と指定しちゃったらおかしいのはわかりますよね?
オペランドの間すべてに空白(とゆーか区切りを示す文字)を挟むようにすれば、それぞれ見た目にも明確になるのでその方がよいと思います。
written by へにっくす

cookierinon

Re: 逆ポーランド記法の電卓

#6

投稿記事 by cookierinon » 11年前

無事トレースでき、空白による区切りも理解できました。
思えば、
while ((s[0] = c = getch()) == ' ' || c == 't')
;
の箇所などを注意深く読むことで空白による区切りも気づかなければならかったのでしょう。
boxさん、へにっくすさん、ありがとうございました。

閉鎖

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