2010年1月30日土曜日

第134回目 計算式の分析2

○第134回目 計算式の分析2

 第107回から113回あたりで計算式の分析と称して、主に計算式の中の項目別の値を取得して一覧表にするという機能を作りました。
 これに似たもので、画面に表示するものを作ってみたらどうかということです。
 複雑な計算式をチェックするの場合、その中の項目ごとの値が必要となります。それを任意に取って、画面に表示できないかというものです。記録としてまでは必要ないかも、というレベルを中心にします。もちろん後で利用可能なようにクリップボードには貼付けてください。でもこれはあくまでも、付加機能。
 
 具体的にどういうことかをもっと詳しくしましょう。
 このような計算式があったとします。
 =IF(M13<>0,ROUND(O77*(G13+0.5)*12*LOOKUP(INT(F13+G13+0.5),○○シート!$AU$7:$AU$63,○○シート!$AV$7:$AV$63)/1000,1),0)


 ここに区切り記号を入れて(これは手作業。入れる場所は任意)、
 =IF(【M13<>0】,ROUND(【O77】【*(G13+0.5)】*12*【LOOKUP(INT(F13+G13+0.5),○○シート!$AU$7:$AU$63,○○シート!$AV$7:$AV$63)】/1000,1),0)
それを分解し、
 =IF(
 【M13<>0】
 ,ROUND(
 【O77】
 【*(G13+0.5)】
 *12*
 【LOOKUP(INT(F13+G13+0.5),○○シート!$AU$7:$AU$63,○○シート!$AV$7:$AV$63)】
 /1000,1),0)
【 】で囲まれた部分の計算式の結果を表示する
、というものです。

 これは前回のユーザフォームのテキストボックスを使うとできます。
 テキストボックス内でコピーすれば、最後の部分を
 【INT(F13+G13+0.5)】
 【LOOKUP(INT(F13+G13+0.5),○○シート!$AU$7:$AU$63,○○シート!$AV$7:$AV$63)】
 /1000,1),0)
と、二重に記述することも可能です。
あわせて元の計算式も表示します。


(注)区切り記号は、入力しやすいものにするために、//とします。
  計算結果はすべての行について求めます。計算式の形式が整っていないのはエラーになりますので、そのときはブランク表示となります。

 このマクロは目新しいことはアイデアだけですので、マクロを組みことに関しては、今までのものをつぎはぎすれば大丈夫です。

 1.//を探し、計算式をその部分で分解する。//がなくなるまで繰り返す。最後は注意。
 2.計算結果を求める。
 計算結果は、使われそうもないセルに、分解した計算式を入れて求めます。計算式の形式を満たしていないものはエラーになりますので、エラーがおきたら、その計算結果はブランクだとしてください。
 3.表示内容を作成する。
 表示内容は、
  元の計算式と改行
  特別な区切り記号(**********)と改行
  分解したひとつずつごとに改行し、分解した部分と計算結果
 とします。
   例
    【INT(F13+G13+0.5)】 =⇒24
    【LOOKUP(INT(F13+G13+0.5),○○シート!$AU$7:$AU$63,○○シート!$AV$7:$AV$63)】
     /1000,1),0) =⇒0.253
 4.終了かどうかの入力
 処理は一回限りとしてもいいでしょうし、連続処理としてもいいと思います。
 連続処理とは、もう一度もとの計算式に戻り、区切り記号を入れていくというパターンと、これまで区切り記号を入れた結果を用いて、さらに区切り記号を入れていくというパターンとなります。戻る先が異なりますので注意してください。
 めんどくさいときは、最後のパターンのみとします。また最初からマクロを動かせば、一番目のパターンとなります。

 ※特別な区切り記号(**********)探し、その前後で区切ることになりますが、その際、その特別な区切り記号は10文字分ですが、その後に改行がありますので、12文字分あると思ってください。特別な区切り記号がある場所+13文字分から先が、これまでの処理結果となります。これを間違えると、不可解な改行が追加されていってしまいます(簡単なマクロなのですが、この点で引っかかってしまいました。一文字として認識していました)。

2010年1月17日日曜日

第133回目 計算式作成のお手伝い(一般的な計算式、究極モドキ3)

○第133回目 計算式作成のお手伝い(一般的な計算式、究極モドキ3)

 第131回目での質問です。
□質問があります。計算式を修正する時に、計算式が長い場合は、全部が表示できないのではないでしょうか。また、文字数の制限はあるのでしょうか。
=⇒inputboxでの制限文字数は256バイトのようです。それより長いと駄目です。また、ご指摘のとおり、表示幅が小さいため、一覧できない場合があります。
□このマクロは、複雑な計算式の場合に力を発揮するというものだと思いますが、矛盾しませんか。

 表示幅の問題とプラスアルファを紹介します。
 inputoboxでの表示領域を拡大できないか、という問題の対応(アイデアレベル)は、簡単でした。
inputoboxの代わりに、ユーザーフォームのテキストボックスを使えばよいのです。
ユーザーフォームのマクロとモジュールでのマクロが行き来しますので、共通の変数はパブリック変数にしてください。、

テキストボックスはできるだけ大きく設定したほうがいいでしょう。ボタンは、『続ける』と、『終了』の二つです。ここでは、文字のフォントとサイズの指定が可能です。文字を大きく、そして自分の好きな字体での表示が可能となります。

イベントは4つになります。『初期化と値のセット』、『テキストの修正があった』、『続けるボタンが押された』、『終了ボタンが押された』、です。
 Private Sub UserForm_Initialize() 'ユーザーフォームを初期化する
  TextBox1.MultiLine = True '折り返し表示
  TextBox1.Text = p_aa '値をセットする。p_aaに計算式が入っている
 End Sub

 Private Sub TextBox1_Change() 'ボックス内に動きがあると動く
  p_aa = TextBox1.Text 'テキストを取得
 End Sub

 Private Sub CommandButton1_Click()
 '続けるボタンがクリックされた
  Unload Me 'ユーザーフォームを閉じる
  p_flag = 0
 End Sub

 Private Sub CommandButton2_Click()
 '終了ボタンがクリックされた
  Unload Me 'ユーザーフォームを閉じる
  p_flag = 9
 End Sub

表示するフォントに関しては、ユーザフォームを設計する画面に一緒に出てくるプロパティのFontで指定してください。

モジュール(通常のマクロを記述するところ)側では、こんな感じです。
'計算式へ文字の付加
p_aa = 複雑カッコ文の解析(keisansiki) 'この関数はカッコをランク付けして置き換えるものです。
UserForm3.Show
'ここでUserForm3のマクロに飛ぶ。そのマクロが終了すると戻る。
a = 複雑カッコをもとに戻す(p_aa)  'この関数はカッコをすべて小カッコに直すものです。
p1 = InStr(a, "=")
If p1 <> 0 Then 'イコールがあると置き換え、ないと末尾に追加とした
keisansiki = a
Else
keisansiki = keisansiki & a
End If
If p_flag = 9 Then Exit Do

 プラスアルファとは、上記でいうと"複雑カッコ文の解析(keisansiki)"という部分です。 第124回でカッコの置き換えをしましたが、それをすると複雑な計算式の構造が、少しわかりやすくなります。上記の例では、この機能を関数化して利用しています。反対の処理、すべて小カッコに戻すも同様です。
 
【今回のポイント】
・かなり長い計算式でも一覧できる。
・複雑な構造の計算式でも、カッコをランクごとに変えて表示するので、構造がわかりやすくなる。
・字体と字の大きさが代えられるので、見やすくなる。

※今回のやり方、ユーザフォームを使って表示領域を大きくするという基本の考え方は、他にも使えるかもしれません。
 Msgboxを使用すると、その表示位置がコントロールできません。大体は画面の真ん中に来るようです。どこかほかに出てきてほしいということが多々あると思います。これをユーザフォームを使うことで対応することが可能です。位置はモジュールマクロの中で記述します。

2010年1月10日日曜日

第132回目 罫線編-3

○第132回目 罫線編-3

 罫線については、既に第52回目と第53回目ちょっとした小物、罫線編で紹介しましたが、多少の機能を追加したいと思います。
 1.五とびに横線を引く(五は一例。飛び飛びに引く機能)
 2.細線の横線を引く
 3.太線の枠線を引く
 4.罫線ダイアログを出す
 をご紹介しました。
 通常の処理とこれらのマクロを兼用して処理をしてきましたが、もう少しマクロの範囲を拡げて、通常の処理を使わないでよいという感じにまでちかづければと思いました。
そこで、
 1.指定範囲の上側または左側の罫線を引く・削除する。
 2."太線の枠線を引く"を拡張して、一つの表の中をブロックに分け、そのブロック全部に枠線を引く。これによって表にかなりのアクセントがつきます。

 1.について
 コーディングは簡単です。
 入力値として、上側に罫線を引くを11として、左を13、上を削除を91、左を93とします。単純なケースわけの処理となります。(さらにこれを拡張して、14では上と左を同時に引く、94では上と左を同時に消す、というのを考えるといいでしょう)


上を引く場合の例です。
'上を引く
Case "11"
With Selection.Borders(xlEdgeTop)
.LineStyle = xlContinuous
End With
With Selection.Borders(xlInsideHorizontal)
.LineStyle = xlContinuous
End With
これをもとに
 xlEdgeTop=⇒xlEdgeLeft
 xlInsideHorizontal=⇒xlInsideVertical
 xlContinuous=⇒xlNone(これは削除です)
 に変えるとできます。

2.について
 選択した範囲の枠線を太線で引くは次のとおりとなります。
Selection.BorderAround LineStyle:=xlContinuous
Selection.BorderAround Weight:=xlMedium
このマクロは全体の範囲を選択した後から動かします。
入力値は、たて見出しの列数、よこ見出しの行数、内部ブロックの列数、行数。これを-で区切って入力。不要な場合は、該当するところを0といれる。1-3-4-0とか。
 入力値からそれぞれの値を取り出すことについては省略します。
 それぞれを、tate_m、yoko_m、data_b1、data_b2とします。

さて、たて見出しの範囲は次のとおりとなります。
Range(Cells(gyo1, retu1), Cells(gyo9, retu1 + tate_m - 1)).Select
 よこ見出しは
Range(Cells(gyo1, retu1), Cells(gyo1 + yoko_m - 1, retu9)).Select
 内部のたてブロックは
ブロックの個数は、商で求めます。
p1 = (retu_cnt - tate_m) \ data_b1
となり、この回数分繰り返します。
For i = 1 To p1 Step 1 'p1がゼロの場合は引かない
Range(Cells(gyo1, retu1 + tate_m + (i - 1) * data_b1), _
Cells(gyo9, retu1 + tate_m + i * data_b1 - 1)).Select
Selection.BorderAround LineStyle:=xlContinuous
Selection.BorderAround Weight:=xlMedium
Next
 内部のよこブロックは、たてと同様ですので考えてみてください。
当然各数値が0であった場合は処理をしませんので、if文できいてください。

※補足
線の太さの種類としては、太いもののほかに、通常のもの、細いものがあります。
 それらは、Selection.BorderAround Weight:=のところを、xlMedium、xlThin、xlHairlineとすればいいのです。
これらの内部コードは、太線=-4138、通常線=2、細線=1となっていますから、これを使って、Selection.BorderAround Weight:=-4138とすれば同じになります。つまり、太さの調整も簡単に出来るということです。

2010年1月2日土曜日

○第131回目 計算式作成のお手伝い(一般的な計算式、究極モドキ2)

○第131回目 計算式作成のお手伝い(一般的な計算式、究極モドキ2)

 今回のテーマは、以下のコーディングに関することです。
 1.座標を入力する際に、1回で複数の座標を演算子でつないでいれることができる(この入力内容は計算式としての要件を備えている必要がある)。
 2.計算式の途中に、座標を入れることが出来る。途中の座標を修正することが出来る。

 1.のポイント
 セルの設定の際に、Application.InputBox文のタイプ(Type:=0)として、計算式を指定する(既存のマクロを修正したので、変数名は多少ちぐはぐ)。
 その結果の受け入れも、計算式として受け入れる。計算式なので、先頭のイコールがついているため、それを取り除いて使用する。
'座標の設定
セル範囲 = Application.InputBox(Prompt:="使用するセルを指定してください。終了はキャンセルキー。", Default:=in_rrcc01, Left:=10, Top:=2, Type:=0)
If セル範囲 = False Then errflag = 0: Exit Do
Range(in_rrcc0).Formula = セル範囲
in_rrcc2 = Range(in_rrcc0).Formula
in_rrcc2 = Mid(in_rrcc2, 2) =⇒これが入力された座標等の情報

 この複数座標等の情報から、次回の基点となる座標を抜出す。これは単独の場合と、コロンがあり範囲指定の場合の二つに分けています。
【単独の場合】
'計算式の入力は必ず座標で終わり、その直前は四則演算子のみとする。これ以外はエラーとする。
p1 = InStrRev(in_rrcc2, "+")
p2 = InStrRev(in_rrcc2, "-")
p3 = InStrRev(in_rrcc2, "*")
p4 = InStrRev(in_rrcc2, "/")
If p1 + p2 + p3 + p4 = 0 Then
in_rrcc01 = in_rrcc2
ElseIf p1 > p2 And p1 > p3 And p1 > p4 Then
in_rrcc01 = Mid$(in_rrcc2, p1 + 1)
ElseIf p2 > p3 And p2 > p4 Then
in_rrcc01 = Mid$(in_rrcc2, p2 + 1)
ElseIf p3 > p4 Then
in_rrcc01 = Mid$(in_rrcc2, p3 + 1)
Else
in_rrcc01 = Mid$(in_rrcc2, p4 + 1)
End If
【範囲指定の場合】
'コロンと最後のカッコを考慮し、座標を切り出す
p1 = InStrRev(in_rrcc01, ":")
If p1 <> 0 Then in_rrcc01 = Mid$(in_rrcc01, p1 + 1)
p2 = InStrRev(in_rrcc01, ")")
If p2 <> 0 Then in_rrcc01 = Left$(in_rrcc01, p2 - 1)
in_rrcc01が次回の基点となるセルの座標です。

 2.のポイント
 既存の計算式に特殊記号("..")がある場合は、それを入力値に置き換えます。
'..がある場合は、そこを置き換える。
p0 = 0
p1 = InStr(keisansiki, "..")
If p1 = 0 Then
keisansiki = keisansiki & in_rrcc2 =⇒in_rrcc2は入力された座標等の情報
Else
Do While 1 置換え箇所が複数あることがあります
keisansiki = Left$(keisansiki, p1 - 1) & in_rrcc2 & Mid$(keisansiki, p1 + 2)
p1 = InStr(keisansiki, "..")
If p1 = 0 Then Exit Do
Loop
End If

 注意点;このマクロで追加・修正した座標は相対座標になっていますので、絶対座標にしたい時は、キーボードによる文字列の追加・修正の際、又は終了後に修正してください。

□質問があります。計算式を修正する時に、計算式が長い場合は、全部が表示できないのではないでしょうか。また、文字数の制限はあるのでしょうか。
=⇒inputboxでの制限文字数は256バイトのようです。それより長いと駄目です。また、ご指摘のとおり、表示幅が小さいため、一覧できない場合があります。
□このマクロは、複雑な計算式の場合に力を発揮するというものだと思いますが、表示できる長さに制約があるということですと、うまくいかないのではないでしょうか。
=⇒確かにご指摘のとおりのことが起こります。その対応として、ヒント的なものをつかんでいます。
□それを早く教えてください。このままではアイデアはいいが…、ということになってしまうのではないでしょうか。
=⇒ご指摘のとおりだと思います。早めに提示できるようにします。