パソコン活用研究5番街(Visual Basic、Excel(VBA)、BASIC プログラミング研究)


VB.NETで計算機を作る(とりあえず完成形)
〜小数点を使えるようにする、べき乗演算にも対応
(準備中)


前回(「VB.Netで計算機を作る(乗算・除算)」)では、乗算・除算と括弧( )の実装もして
四則演算までできるようにしてみました。

ここらで、ひとまず完成形に向けて仕上げをしていきます。
1.式の中に小数点の数値も使えるようにする
2.べき乗の演算にも対応する
3.異常入力値の対処もきちんとする


1.
前回(「VB.Netで計算機を作る(乗算・除算)」)のプログラムでも、入力された式の中の数値を保持する変数や
演算結果を保持する変数はdouble型を使っていましたが、字句解析で小数点を解析する処理を入れていなかった
ので、式の中に小数が現れると不正な文字として処理されていました。

1.5を入力したところ。
dot( . )のところで処理を中断し、結果として1が出力されている。


小数点の処理をし、小数を取り扱えるようにするため字句解析の処理ルールは以下のようにしました。
正常な式に対して正しく計算結果を出すことしか考えてないので、異常値(式)に対してのチェックはかなり甘いです。
以下の表の空欄の個所は何も処理を実装していないので、このあたりの異常値(式)には対応してません。
このあたりは最後に仕上げます。

isnum=0 isnum=1 isnum=2 isnum=3
数値0〜9 数値の始まりとして認識し、
isnum=1に変更する
数値の続きとして処理 小数部が始まったとして処理。
isnum=3とする。
数値の続きとして処理
- 負の記号として認識し、数値の始まりとして
isnum=1
に変更する
数値が終了したとみなし、posを一つ加算し、
演算子としてトークン化する。更にposを一つ
加算してisnum=0に変更
err=1(エラーフラグを立て)
Exit For(Forループを抜ける)
数値が終了したとみなし、posを一つ加算し、
演算子としてトークン化する。更にposを一つ
加算してisnum=0に変更
+ * / 数値が終了したとみなし、posを一つ加算し、
演算子としてトークン化する。更にposを一つ
加算してisnum=0に変更
err=1(エラーフラグを立て)
Exit For(Forループを抜ける)
数値が終了したとみなし、posを一つ加算し、
演算子としてトークン化する。更にposを一つ
加算してisnum=0に変更
( (としてトークン化し、posを1つ加算する。 err=1(エラーフラグを立て)
Exit For(Forループを抜ける)
) 数値の終了とみなし、posをひとつ加算する。
)としてトークン化する
err=1(エラーフラグを立て)
Exit For(Forループを抜ける)

数値の終了とみなし、posをひとつ加算する。
)としてトークン化する
小数点 ( . ) 小数点として処理
isnum=2とする
err=1(エラーフラグを立て)
Exit For(Forループを抜ける)
err=1(エラーフラグを立て)
Exit For(Forループを抜ける)
空白 読み飛ばす 読み飛ばす 読み飛ばす 読み飛ばす
その他文字 err=1(エラーフラグを立て)
Exit For(Forループを抜ける)
err=1(エラーフラグを立て)
Exit For(Forループを抜ける)
err=1(エラーフラグを立て)
Exit For(Forループを抜ける)
err=1(エラーフラグを立て)
Exit For(Forループを抜ける)

変更した字句解析のコードです。

       For i = 0 To statement.Length - 1
            r = checktype(statement.Chars(i))
            If r = type.space Then GoTo L1 : 
            If r = type.bad Then Console.WriteLine("{0}:Bad expression", i) : err = 1 : Exit For
            If (isnum = 1 Or isnum = 3) And r = type.number Then tok(pos).str = tok(pos).str + statement.Chars(i) : tok(pos).v = Double.Parse(tok(pos).str) : GoTo L1
            If isnum = 1 And r = type.dot Then tok(pos).str = tok(pos).str + statement.Chars(i) : isnum = 2 : GoTo L1
            If (isnum = 1 Or isnum = 3) And r = type.rparen Then pos = pos + 1 : tok(pos).str = statement.Chars(i) : tok(pos).type = r : GoTo L1
            If (isnum = 1 Or isnum = 3) And (r = type.add_op Or r = type.sub_op Or r = type.mul_op Or r = type.div_op) Then pos = pos + 1 : tok(pos).str = statement.Chars(i) : tok(pos).type = r : pos = pos + 1 : isnum = 0 : GoTo L1
            If isnum = 2 And r = type.number Then tok(pos).str = tok(pos).str + statement.Chars(i) : tok(pos).v = Double.Parse(tok(pos).str) : isnum = 3 : GoTo L1
            If isnum = 2 And r <> type.number Then err = 1 : Console.WriteLine("{0}: Bad expression", i) : Exit For
            If isnum = 3 And r = type.dot Then err = 1 : Console.WriteLine("{0}: too many dot", i) : Exit For
            If isnum = 0 And r = type.lparen Then tok(pos).str = tok(pos).str + statement.Chars(i) : tok(pos).type = r : pos = pos + 1 : GoTo L1
            If isnum = 0 And r = type.number Then tok(pos).str = tok(pos).str + statement.Chars(i) : tok(pos).type = type.number : tok(pos).v = Integer.Parse(tok(pos).str) : isnum = 1
            If isnum = 0 And r = type.sub_op Then tok(pos).str = tok(pos).str + statement.Chars(i) : tok(pos).type = type.number : isnum = 1
L1:
        Next


2. べき乗の演算に対応する。

べき乗演算のための関数 function pow() を追加し、
再帰下降パーサを
expression --> term --> pow --> primary_expression とします。

変更は、term()とpow()だけなので、そこのコードのみ掲載します。

Function term() As Double
        Dim v1, v2 As Double
        Dim t As token

        v1 = pow()
        Console.WriteLine("term v1:{0}", v1)
        While (1)
            t = get_token()
            Console.WriteLine("term {0}: {1} : {2} ", t.str, t.type, t.v)
            If (t.type <> type.mul_op And t.type <> type.div_op) Then unget_token() : Exit While
            v2 = pow()
            Console.WriteLine("term v2:{0}", v2)
            If t.type = type.mul_op Then v1 = v1 * v2
            If t.type = type.div_op Then v1 = v1 / v2
            Console.WriteLine("term calculated  v1:{0}", v1)
        End While

        Return v1
    End Function

    Function pow() As Double
        Dim v1, v2 As Double
        Dim t As token

        v1 = primary_expression()
        Console.WriteLine("pow v1:{0}", v1)
        While (1)
            t = get_token()
            Console.WriteLine("pow {0}: {1} : {2} ", t.str, t.type, t.v)
            If (t.type <> type.pow_op) Then unget_token() : Exit While
            v2 = primary_expression()
            Console.WriteLine("pow v2:{0}", v2)
            If t.type = type.pow_op Then v1 = v1 ^ v2
            Console.WriteLine("pow calculated  v1:{0}", v1)
        End While

2^0.5*3を計算したところ。
^0.5とは0.5乗すなわち平方根を求めている。
小数も使えて、べき乗計算もできるようになった。



3. 異常値(式)入力時の対処

四則演算計算機のアルゴリズムのお勉強という目的だったので、ここまで異常値の入力時のチェックは
あまりしてなかったのですが、ひとまず完成形を作ってみようということで、異常値入力時のチェックに
最後にとりかかります。
が、これは次回に回します。






TopPage > Visual BasIc&Excel活用研究目次