C言語アルゴリズム-計算式
前へ 目次へ 次へ 

3 計算式の評価

3.1 計算式

 計算式を演算子の優先順位にしたがって計算し、結果を表示する。演算子には、四則演算(+,-,*,/)が使用でき、括弧による演算順位の変更もできる。扱える数値は、実数(double型)で1E2のような指数表現も可能である。このようなプログラム(関数)を作成する。

例1) 11 + 20 * 30     答え=611
例2) (1.12 + 2.24 - 3) * 1e3     答え=360

 計算式を評価するには、読み込んだ計算式を数値と演算子に分解しなければならない。分解して単語(数値と演算子)を取り出す関数get_tokenについては別ファイル(gettoken.c)として作成してあるものとする。
 例1)の場合、関数get_token()で計算式を 11, +, 20, *, 30 に分解しながら1つずつ取り出して、下のチャートにしたがって処理をしていく(図の下に処理の説明)。
 下のチャートは、流れ線に沿って処理を進めていく。○は末端記号といい実際の文字・記号であり、□は別のところで定義されているもので非末端記号という。

Eval

項A、項BなどのA,Bは、説明のために便宜的に付けたもので、両者に違いはない。

  1. 計算式から11を読み込む。
      式→ 項A(項)因子A(因子) へと進むと(数値)に合致する。 次の+を読み込む。
  2. 因子A へ戻ると矢印の方向は、(×)と(÷)の方向かまっすぐの方向にある。先に読み込んだ文字は、+で(×)と(÷)に一致しないのでまっすぐ進む(項A に戻る)。
      項A へ戻り、+の方向へ進む。
      次の20を読み込む。このとき、項A の値は11である。   項A = 11
  3. 流れに沿っていくと、また 項B(項)因子A(因子) へと進み数値に合致する。
      次の*を読み込む。
  4. 因子A に戻り、(×)の方向へ進む。 次の30を読み込む。   因子A = 20
  5. 因子B(因子) に進むと(数値)に合致する。
      読み込むものは終わりである。
  6. 因子B に戻る。   因子B = 30
  7. 因子A因子B の乗算を行い、項B に戻る。   項B = 600
  8. 項A項B の加算を行う。   式 = 611

 この手法を再帰的下向き構文解析という。この他に計算式を処理する方法として、逆ポーランド記法(これも再帰的下向き構文解析の手法を使用している)等がある。

 

入力された計算式(数式)を評価(evaluation)する。

 コンパイルは、次のようにする。

MS-DOS> cc eval.c gettoken.c
または
MS-DOS> cc  -c  gettoken.c
MS-DOS> cc  eval.c  gettoken(.obj)
UNIX% gcc -o eval eval.c gettoken.c

UNIX% gcc  -c -o gettoken.o gettoken.c
UNIX% gcc  -o eval  eval.c  gettoken.o

[eval.c]

 1:
 2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "gettoken.h"

/* 関数プロトタイプ宣言 */
double expression(void);   /* 式 */
double term(void);         /* 項 */
double factor(void);       /* 因子 */
double number(void);     /* 数値データ */

void main(void)
{
    double  ans;

    while(1){
        printf("計算式 : ");
        gets(gt_line);
        get_token();
        if(*token == '\0')
            break;
        ans = expression();
        printf("答え=%g\n", ans);
    }
}
double expression(void)        /* 式 */
{
    double  ans;

    ans = term();
    while(1){
        if(*token == '+'){
            get_token();
            ans = ans + term();
        }else if(*token == '-'){
            get_token();
            ans = ans - term();
        }else
            break;
    }
    return(ans);
}
double term(void)        /* 項 */
{
    double  ans, x;

    ans = factor();
    while(1){
        if(*token == '*'){
            get_token();
            ans = ans * factor();
        }else if(*token == '/'){
            get_token();
            x = factor();
            if(x == 0){
                printf("0では割れない\n");
                exit(1);
            }
            ans = ans / x;
        }else
            break;
    }
    return(ans);
}
double factor(void)        /* 因子 */
{
    double  ans;

    if(*token == '('){
        get_token();
        ans = expression();
        if(*token != ')'){
            printf(")がない\n");
            exit(1);
        }
        get_token();
        return(ans);
    }else
        return(number());
}
double number(void)        /* 数値データ */
{
    double  ans;
    char    sign;

    if(*token == '+' || *token == '-'){
        sign = *token;
        get_token();
    }
    ans = atof(token);
    get_token();
    if(sign == '-')
        return(-ans);
    return(ans);
}


実行結果
計算式 : 11+20*30
答え=611
計算式 :


 繰り返し実行できるようになっている。「計算式 : 」でリターンキーだけを押せばプログラムは終了する。


前へ 目次へ 次へ 
Copyright © 2001 Hiroshi Masuda 

 

 

inserted by FC2 system