パソコン活用研究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活用研究目次