2009年8月22日土曜日

第126回目 計算式の分析;if関数(その前に)

○第126回目 計算式の分析;if関数(その前に)

 計算式の分析とは、何ぞ。
 例えば、複雑なif関数の場合、各項目の値を取得して、計算式のチェックに役立てようとするものです。

□『第111回目 計算式で参照されている場所の情報を集めた一覧表』でやったような気がしますが、それとはどう違うのですか。
=⇒いい質問ですね。
 違うところは確かにあるのですが、それほど取り上げるほどのものがあるかと、言われると…。
 まあ、いろいろなことにチャレンジしてやってみたということで、理解願います。

 =INT(IF(B23>0.1,INT(B23*100),IF(B23>0.01,INT(B23*1000),IF(B23>0.001,INT(B23*10000),INT(B23*100000))))/10)*10+(INT((B23-INT(B23*10)/10)*90)+1)
 という計算式がありました。
 『計算式で参照されている場所の情報を集めた一覧表』では、計算式の値、数値入り計算式、使われている場所の値とその計算式を示すものです。
 具体的には、
・値 12
・数値入り計算式 ***=INT(IF(0.119291635991611>0.1,INT(0.119291635991611*100),IF(0.119291635991611>0.01,
INT(0.119291635991611*1000),IF(0.119291635991611>0.001,INT(0.119291635991611*10000),
INT(0.119291635991611*100000))))/10)*10+(INT((0.119291635991611-INT(0.119291635991611*10)/10)*90)+1)
・使われているセル
乱数 (3)---B23
 計算式 ***=RAND()
 値 0.119291636

 この例の場合は、元となっている値が0.119…なので、一番最初のif文が成立して、計算結果としては、12となったということがわかります。

 この場合は、if文の値、そして各項目の値がわかりません。今回のテーマは、これを求めようとするものです。
・IF(B23>0.1,INT(B23*100),IF(B23>0.01,INT(B23*1000),IF(B23>0.001,INT(B23*10000),INT(B23*100000))))
・B23>0.1
・INT(B23*100)
・IF(B23>0.01,INT(B23*1000),IF(B23>0.001,INT(B23*10000),INT(B23*100000)))

 さらに、この場合は,if文がさらにありますので、次のif文、次のif文と分解しいって、上記の値を求めるということとなります。

□最後の項目はif文ではないのですが、これはどうなりますか?
=⇒if文ではないので、無視しました。難しくはないのですが、複雑になるので、分析対象はif文だけにしています。課題として認識しておきます。

 イメージが決まりましたので、具体的に進めていきましょう。
 まずは、カッコの分析からです。
 カッコの分析とは、何番目のカッコ開くが、何番目のカッコ閉じるに対応しているかというものです(これに"カッコの深さ"を入れていくのはそれほど難しくありません)。 発想としては、始めすべてのカッコ開く、カッコ閉じるの場所を取得し、それらの関係から、カッコの対応をつけていくことが考えられます。
 対応するカッコの間には、カッコ開く、カッコ閉じるがまったくないか、同数あるかとなります。
 と思っていましたが、さらに違う発想もあるということがわかりました。

 その違う発想とは、
 1.最後のカッコ開くとその次のカッコ閉じるは対応している
 2.このカッコ開くを含めて、それ以降を削除したもので、同じようにやっていくと、対応するカッコが見つかる時があるが、それも対応するカッコとなる。ない場合は3へ。
 3.対応するカッコがわかったら、もとの計算式の中で、そのカッコを違う記号に置き換えておく。これが一番深いレベルのカッコです。
 4.1.に戻り、カッコを置き換えた計算式を元に同様の処理をする。これが次のレベルのカッコです。
 5.カッコがなくなるまで続けます。

【一例】
 IF(B23>0.01,INT(B23*1000),IF(B23>0.001,INT(B23*10000),INT(B23*100000)))
カッコを@で置き換えていきます。

1. IF(B23>0.01,INT(B23*1000),IF(B23>0.001,INT(B23*10000),INT@B23*100000)))
2. IF(B23>0.01,INT(B23*1000),IF(B23>0.001,INT(B23*10000),INT@B23*100000@))
3. IF(B23>0.01,INT(B23*1000),IF(B23>0.001,INT(B23*10000),INT
1. IF(B23>0.01,INT(B23*1000),IF(B23>0.001,INT@B23*10000),INT
2. IF(B23>0.01,INT(B23*1000),IF(B23>0.001,INT@B23*10000@,INT
3. IF(B23>0.01,INT(B23*1000),IF(B23>0.001,INT

対応するカッコがなくなったので、カッコを整理する。
4. IF(B23>0.01,INT(B23*1000),IF(B23>0.001,INT@B23*10000@,INT@B23*100000@))
カッコを\で置き換えていきます。
1. IF(B23>0.01,INT(B23*1000),IF\B23>0.001,INT@B23*10000@,INT@B23*100000@))
2. IF(B23>0.01,INT(B23*1000),IF\B23>0.001,INT@B23*10000@,INT@B23*100000@\)
3. IF(B23>0.01,INT(B23*1000),IF
1. IF(B23>0.01,INT\B23*1000),IF
2. IF(B23>0.01,INT\B23*1000\,IF
3. IF(B23>0.01,INT

対応するカッコがなくなったので、カッコを整理する。
4. IF(B23>0.01,INT\B23*1000\,IF\B23>0.001,INT@B23*10000@,INT@B23*100000@\)
カッコを#で置き換えていきます。
1. IF#B23>0.01,INT@B23*1000@,IF\B23>0.001,INT@B23*10000@,INT@B23*100000@\)
2. IF#B23>0.01,INT@B23*1000@,IF\B23>0.001,INT@B23*10000@,INT@B23*100000@\#
 終了
 (わかりやすくするために、各回での置換え記号を変えています)。

 カッコの深さは3となっています。
 まずは、これをマクロに組みます。対応するカッコは、二つの配列変数に始めの位置、終わりの位置として、取得しておきます。
 (深さも同時に取得しておくと、カッコを順番に、小カッコ、中カッコ、大カッコ…と変えることができます(小カッコは変える必要はありませんが…))

 '計算式を取得する
keisansiki = "=(A20+B20)*(C20+(D20-E20)/(F20+(G20-(H20+J20))))"
keisansiki = Mid$(keisansiki, 2) 'イコールをとる
keisansiki0 = keisansiki
keisansiki2 = keisansiki
a = keisansiki

計算式としては、オリジナルのもの(keisansiki0)、カッコを仮に変えたもの(keisansiki)、カッコを正式なものに変えたもの(keisansiki2)、判断に使うもの(a)と4つ作りました。

'順番にしたカッコの取得
For i = 1 To 10
kakko1(i) = Mid$("({[〔「『〈《【【", i, 1)
kakko9(i) = Mid$(")}]〕」』〉》】】", i, 1)
Next
'計算式の中から(の位置を取得する。後ろから取得していく
m = 0
rebel = 1
Do While 1
p1 = InStr(keisansiki, "(")
If p1 = 0 Then Exit Do
Do While 1
p1 = InStrRev(a, "(")
If p1 = 0 Then Exit Do
p2 = InStr(Mid$(a, p1 + 1), ")") 'これ以降にカッコ閉じるがあるか
If p2 <> 0 Then
m = m + 1  'カッコの番号
pk1(m) = p1 'カッコ始めの位置
pk9(m) = p1 + p2 'カッコ閉じるの位置
kakko_rebel(m) = rebel 'カッコの深さ
keisansiki = Left$(keisansiki, p1 - 1) & "<" & _
Mid$(keisansiki, p1 + 1, p2 - 1) & ">" & _
Mid$(keisansiki, p1 + p2 + 1) '仮のカッコに置換
keisansiki2 = Left$(keisansiki2, p1 - 1) & kakko1(rebel) & _
Mid$(keisansiki2, p1 + 1, p2 - 1) & kakko9(rebel) & _
Mid$(keisansiki2, p1 + p2 + 1) '正式のカッコに置換
End If
a = Left$(a, p1 - 1)
Loop 'この深さのカッコの取得終了
a = keisansiki '既に処理されたカッコは、小カッコから仮のものに置換えられている
rebel = rebel + 1 '次の深さのカッコを探しにいく
Loop
m_cnt = m 'カッコの組数
rebel_cnt = rebel - 1 'カッコの深さ

【入力値と処理結果】
(A20+B20)*(C20+(D20-E20)/(F20+(G20-(H20+J20))))
   ↓
 (A20+B20)*〔C20+(D20-E20)/[F20+{G20-(H20+J20)}]〕

※ここまでの機能だけでも、カッコが多い計算式を見るときにとても便利です。

0 件のコメント: