いらっしゃいませ、277619番目のお客様。  

[Excel]実用(?)ヒント集

このページに含まれるプログラム、ファイル等については、

  1. 一応、著作権は放棄してません。転載等される方は私にご連絡ください。
  2. 内容については万全を期していますが、利用については自己責任でお願いします。いかなる損害にも責任を負いません。
  3. 動作確認は、(1)Windows 98+Excel 2000; (2)Windows XP+Excel 2002で行っています。
以上の点にご注意願います。


目次

  1. 表の行数を増やす
  2. 自作関数を他のワークシートで使う
  3. グラフのサイズをマクロで変えよう
  4. ダイアログを使ってみよう
  5. マクロのやり直し
  6. あなたはきっと悪くない
  7. セルの参照方法
  8. プロシージャの引数
  9. 進行度をダイアログで表してみよう
  10. Application.Volatile
  11. イベントの停止
  12. ワークシートとの値のやり取り
  13. Runメソッド
  14. 仮想セル
  15. 覚えにくい関数をまとめてみよう
  16. 覚えにくい関数をまとめてみよう(その2)
  17. 覚えにくい関数をまとめてみよう(その3)
  18. 数式でセル参照をしてる?

Office VBAの部屋に戻る

表の行数を増やす(2001/9/1)

今回はプログラムではありませんが、最近知ってとても便利だったことを紹介します。みんな知ってるかもしれませんが。
それは名前に関数が使えること。
表を作っていて、日付や金額等の欄に名前を付けている場合、表が伸びるたびにすべての名前の範囲を変更しているあなた。ちょっとした工夫で自動的に範囲も伸びていきますが、それを怠ったときのメンテナンスが大変。
そんなときは表全体に名前(例えば小遣い帳)を付けましょう。(タイトル行を入れるかどうかはそのときの都合によりますが。) そして、日付の入っている列(例えばA)に名前を付けたい場合、[挿入][名前][定義]で、名前を「日付」と入力し、この名前「日付」の参照範囲を以下のように入力します。

=INDEX(小遣い帳,0,1)

これで、表の範囲の1列目(A)のデータ範囲が名前「日付」で定義されます。
2列目(B)なら、 例えば、名前を「項目」と入力し、次のように入力します。
=INDEX(小遣い帳,0,2)

表の範囲の定義は「小遣い帳」を変更するだけ。便利でしょ。「=」以下には自作VBAも使えますが、再計算同様の問題(参照先の数値の変化に対応しない)が生じることもあります。


目次に戻る

自作関数を他のワークブックで使う(2001/9/29)

さて、下で自作関数の入ったワークブックを作ったわけですが、他のワークブックで使おうと思ったら、 どうしたら良いでしょう?ひとつには、

=test.xls!test()

というようにして使うという方法があります。ただ、この場合は自分で自作関数の入ったファイルを 開ける必要があります。
もうひとつは「参照設定をしてやる」ということです。 やり方は簡単。あらかじめ、関数の入ったファイルを開けておき、VBE(Alt+F11で開くエディタのことです) のプロジェクトウインドウで、自作関数の入ったプロジェクトを使用したいプロジェクトの中に ドラッグするだけです。([ツール][参照設定]でも設定・解除が出来ます。) 注意点としては、二つのファイルのプロジェクト名を別々にしてください。 参照設定の場合はファイルを読み込んだ時点で、自動的に関数の入ったファイルが起動します。 (逆にこのファイルを閉じることが出来ません。)また、関数を呼ぶときも
=test()

で呼び出すことが出来ます。
ここで以下のプログラムがtestpというプロジェクトにあるとしましょう。
Private Function test()
    test = 1
End Function

Public Function test2()
    test2 = 2
End Function

Public Function test3()
    test3 = test + test2
End Function

Private Sub test4()
    MsgBox test
End Sub

Public Sub test5()
    MsgBox test2
End Sub

これを参照設定したファイルではどのFunction/Subが使えるでしょうか?

答えはtest2、3、5です。

まず、Functionのみの話をしましょう。test()と他のものの違いはなんでしょう? みれば分かりますね。test()だけは「Private」なのです。これは記述したファイル(プロジェクト)内で のみ使えるということを示します。「public」あるいは書かない場合はどのファイルからでも参照できることになります。使う場合には
=testp.test2()

として使うことが出来ます。ファイル内に同じ名前の関数がない場合は「testp.」は不要です。
どうして「private」が必要なのでしょう。ひとつには同じ名前が多数存在すると混同をきたすために 共有が不要な関数はprivateにするということがあります。もうひとつは同じプロジェクト内の関数から 呼び出すためだけの関数である場合はprivateにして隠してしまうということです。これは不用意に 関数を呼び出されてエラーが起こってしまったり、重要な変数を書き換えられて誤った動作をしないように するためです。
この「private」「public」は変数でも利用できます。Dimの代わりに
Public i As Integer
Private p As Integer
Private Const s As String = "test"

のように使うことが出来ます。

さて、Subの話ですが、ちょっとだけややこしいです。VBAからは確かにtest4は呼び出せません。 しかし、どのファイルがActiveであろうがExcelのマクロの実行では実行できてしまうのです。
これはそういうものだとするしかないようです。


目次に戻る

グラフのサイズをマクロで変えよう(2001/10/5)

私のVBAプログラミングの方法をここで書きたいと思います。私は解説本等を持っていませんので、勉強方法ともいえます。ポイントは

これにつきます。では、実際に今からマクロを作っていきましょう。まず、 図形の大きさを変えるマクロを作りました。
    Selection.Height = 2 'あくまで例です。

実に簡単です。さて、グラフでも実施しました。…失敗です。ChartAreaのHeightは変更できないのです。…というよりChartAreaって何?
日本語のグラフはChartに読み替えましょう。グラフエリアのことでした。しかし、なぜ設定できないのでしょう???
それはともかく「新しいマクロの記録」でグラフを拡大してみました。
    ActiveSheet.Shapes("グラフ 1").ScaleWidth 0.69, msoFalse, msoScaleFromBottomRight
    ActiveSheet.Shapes("グラフ 1").ScaleHeight 0.73, msoFalse, msoScaleFromBottomRight

ScaleWidthやScaleHeightという見慣れないメソッドがあります。ヘルプで確かめておきましょう。
しかしここで問題はSelectionに置き換えられない、ということです。これでは一般性はまったくないので使えません。
仕方がないので、ヘルプでChartAreaからたぐってHeightが使えるオブジェクトを探しました。答えは二つ上のChartObjectsコレクションがありました。
    Selection.Parent.Parent.Height = 2

これで解決?です。しかし、さらに汎用性を持たせたい。つまり、図だろうがグラフだろうがサイズが変えたい。結果、以下のような感じのプログラムになりました。
    Dim nowShapeRange As ShapeRange
    If TypeName(Selection) = "ChartArea" Then
        Set nowShapeRange = Selection.Parent.Parent.ShapeRange
    Else
        Set nowShapeRange = Selection.ShapeRange
    End If
    nowShapeRange.Height = 30

(ちょっと飛躍していますが。)いかかでしょう。参考になりましたか?


目次に戻る

ダイアログを使ってみよう(2001/11/1)

8/10にもダイアログを使ったプログラムを紹介しています。Excelの「普通」の操作のために作られているので、使い勝手は悪いですが、上手に使えば下手なプログラムを組むより早く目標が達せられます。Border.xlsを試された方はいますか?色を取得するためにダイアログが出てくると思います。こんなもの私には作れません。 ここで、プログラムのミソを記したいと思います。

Option Explicit

Sub test()
    Dim c As Variant
    c = getColor
    If Not IsNull(c) Then MsgBox c
End Sub

Function getColor() As Variant
    If Application.Dialogs(xlDialogEditColor).Show(1) Then
        getColor = ActiveWorkbook.Colors(1)
        ActiveWorkbook.ResetColors
    Else
        getColor = Null
    End If
End Function

If以下のApplication.Dialogs(xlDialogEditColor).Show(1)は「ColorIndexの1番の色を変更するよ」という意味です。キャンセルが押されるとFalseが帰ってきます。で、変更された1番の色を取得してから、リセットするという手順を取っています。キャンセルが押されたら、関数としてはNullを返しています。 いかがですか?


目次に戻る

マクロのやり直し(2001/11/24)

マクロを実行は基本的にやり直すことが出来ません。 あとで、選んだセルが間違っていることに気づいてもどうにもならないのです。 「基本的な命令ばっかり使っているのになぜ??」と思います。
ただし、ユーザーがやり直しを選択した場合に実行するマクロを指定することは可能です。

Dim temp As String

Sub test()
    With ActiveCell
        temp = .Address(External:=True)
        .Value = .Value * 2
    End With
    Application.OnUndo "Undo test", "utest"
End Sub

Sub utest(Optional dummy As Integer = 0)
    With Range(temp)
        .Value = .Value / 2
    End With
End Sub

OnUndoという命令をマクロの最後に記述することで、そのマクロを実行したあとにやり直しをした場合に、この場合ならutestが実行されます。そこに、「このマクロのUndoはこんな動作だな」と思うプログラムを組めば良いわけです。「なんで、マクロって言うの?」「何回もtestを実行したら記憶しているアドレスの関係でUndoの動作がおかしくなるんじゃないの?」と思われるかもしれません。理由は簡単です。
  1. 引数を指定できないので、マクロの実行と結局同じ
  2. マクロ(test)を実行した時点でそれ以前にUndoがさかのぼれなくなる(2度以上Undoが実行できない)ので、一番最後のアドレスを覚えているだけで十分
なのです。しかも、変更する前の状況を保存するとかになるとUndoの動作はかなり大掛かりになります。ですので、「マクロを実行は基本的にやり直すことが出来ない」のです。なんとかなりませんかね。


目次に戻る

あなたはきっと悪くない(2001/11/27)

ExcelVBAでプログラミングする場合、どう考えても正しく動くはずなのにそうならない場合が往々にしてあります。これを「仕様」といいます(ホンマか?)。以下はExcel2000での「仕様」ですので、他のバージョンでは異なるかもしれません。
「2重線で普通の太さで5番の色の枠線」と「普通の太さで2重線で5番の色の枠線」とでは何が違うでしょう?常識で言って何も変わりませんね。変わるわけ無いですよね?
ところが、Excelの世界では通用しなかったりします。

Option Explicit

Sub test()
    Dim b As Border
    Dim w As XlBorderWeight '線の太さ
    Dim l As XlLineStyle    '線の種類
    Dim c As XlColorIndex   '線の色
    w = xlMedium
    l = xlDouble
    c = 5
    Range("A1").Clear
    For Each b In Range("A1").Borders   'お勧め
        With b
            .Weight = w
            .LineStyle = l
            .ColorIndex = c
        End With
    Next b
    Range("A3").Clear
    For Each b In Range("A3").Borders
        With b
            .Weight = w
            .ColorIndex = c
            .LineStyle = l
        End With
    Next b
    Range("C1").Clear
    For Each b In Range("C1").Borders
        With b
            .LineStyle = l
            .Weight = w
            .ColorIndex = c
        End With
    Next b
    Range("C3").Clear
    For Each b In Range("C3").Borders
        With b
            .LineStyle = l
            .ColorIndex = c
            .Weight = w
        End With
    Next b
    Range("E1").Clear
    For Each b In Range("E1").Borders
        With b
            .ColorIndex = c
            .LineStyle = l
            .Weight = w
        End With
    Next b
    Range("E3").Clear
    For Each b In Range("E3").Borders
        With b
            .ColorIndex = c
            .Weight = w
            .LineStyle = l
        End With
    Next b
End Sub

これらで、6つのセルに同じ枠線が引かれる筈なのに違うことが分かってもらえると思います。バグとしか思えませんが。w,l,cの値をいろいろ変えると(初期設定のVBEなら、c以外は自動的に適当な値のリストが出ると思います。)、ちょっと遊べてそして結果に呆れてしまうと思います。
自分の思うとおりにVBAが動作しない場合、特に同じオブジェクトに対して、いろいろなプロパティの値を設定する場合には、設定順序を変えてやるとうまく行くこともあるでしょう。何とかなりませんかね。(前回と同じ引きだ…)


目次に戻る

セルの参照方法(2001/12/1)

VBAでセルを参照するときにどう書きますか?普通はRange("A1")=1とか書きますか?これは書いてはいませんがRange("A1").Value=1と書いていることと同じです。(このValueを「既定のプロパティ」と呼びます。)
セルの参照方法にはValueの他にFormula、Text、Value2があります。これらはどう違うのでしょう?具体例を書いてみます。(Value2はValueとほとんど変わらないので省略します。)

A1: 100
B1: =A1*10	(B1には通貨(¥)の書式を設定してあるとします。)

Msgbox Range("B1").Formula	'"=A1*10"
Msgbox Range("B1").Value	'1000
Msgbox Range("B1").Text	'"\1,000"
つまり、Formulaは計算をしないそのままの文字列、Valueは計算後の値、Textはセルの見た目の文字列です。ちなみに上から分かるようにTextは文字列ですが、表示形式が標準で無い場合、Cstr(Range("A1"))[="1000"]と結果は異なることがあります。では、セルとセルの比較をするとき何を使えば良いでしょうか?
結論はさておき、それぞれの欠点は、
  1. Formula…式が違うと等しくならない。「=1+3」と「'=1+3」(文字列)が区別できない。
  2. Value…見た目が同じでも等しいとならない、逆に見た目が違っても同じになる場合がある。
  3. Text…数値比較は出来ない、処理速度が若干遅い。
となるでしょう。私の結論としては「VBAで処理する部分はValueで、ユーザーに入力してもらう部分はTextで」ということになります。それは入力する人にとって、見えてるものがすべてであって、Excelがどう処理しているかは関係ないからです。例えば、「\1,000」と「"\1,000"」(文字列)は区別が難しいですし、見た目が同じなのに「等しくない」というのは納得しにくいでしょう。(逆にTextで\1000と\1,000が違うというのも納得いかないかもしれませんが、見た目が違うのは分かりますよね。)Valueを使ってて、一番困るのは次の例です。
A1: ロレックス	A2: $1,000
B1: ロルックヌ	B2: \1,000

Msgbox Range("A2")=Range("B2")	'True
これはExcelは通貨を値を1000として、表示形式で通貨単位を表しているからです。
結論としてはありきたりですが、「違いをきっちり把握して、必要に応じたプロパティを使いましょう」ということになるでしょう。


目次に戻る

プロシージャの引数(2001/12/8)

VBAでプログラムを作っていると、「マクロ一覧に出したくない」プロシージャというのが出来てきます。 例えば、

ちなみにExcel内部では、マクロはSubプロシージャで表されます。ここではExcel画面から操作するためのSubプロシージャをマクロと呼んでいます。
ところで、逆にマクロ一覧に載る条件を考えてみましょう。
  1. Subプロシージャであること
  2. Privateでないこと
  3. 標準モジュール内にあること
  4. 引数を持たないこと
以上を全て満たせば、マクロ一覧に載ることになります。反対にどれかひとつでも違うものを作れば、載らないわけです。実際にSub Test()をどうすればよいか書いてみます。

1. Functionにする。(Function Test()にする)
最も簡単です。一覧には載りませんが引数を持たなければ、ちゃんとマクロとして実行することが出来ます。

2. Privateにする。(Private Sub Test()にする)
同一標準モジュール内で使うだけのプロシージャであれば、Sub, Functionに関わらず、Privateにすべきです。ただし、マクロとしても(なぜか)実行することが出来ます。

3. フォーム内のコード、クラスモジュールに書いてしまう。
ボタンを押したときの動作やその後処理など、フォームに関する処理などはフォーム内のコードとして書くことが可能です。極端に言えば、標準モジュール内はForm1.Showだけということも可能です。この場合のプロシージャはマクロとして実行することが出来ません。

4. 引数をつける(Sub Test(Optional dummy As Integer = 0)にする。)
特に引数がいらないSubプロシージャの場合、上記のようにむだな引数をOptionalで設定します。マクロとして実行することも可能です。

これらの説明をさらに反対に考えると、「Functionでも、引数があっても省略可能な引数(Optional)であれば、マクロとして実行が可能である。」ということになります。具体例は、11/21の「表から重複行の削除」をご覧下さい。


目次に戻る

進行度をダイアログで表してみよう(2002/1/27)

TimerForm(ユーザーフォーム)

Option Explicit
Option Base 1

Private Full
Private Chara(3) As String
Private iCon As Integer

Private Sub Btn_Click()
    Me.Hide
End Sub

Public Sub Start(EndValue, Optional Counter = 0#)
    Dim Percent As Double, BarNum As Integer
    Me.Btn.Caption = "中止"
    Full = EndValue
    Chara(1) = "."
    Chara(2) = ".o"
    Chara(3) = ".oO"
    iCon = 0
    Percent = Counter / Full
    BarNum = Fix(Percent * 10)
    Me.LblTxt.Font.Name = "MS ゴシック"
    Me.LblTime.Font.Name = "system"
    Me.LblTxt = Format(Percent, "000%") & " 終了"
    Me.LblTime = String(BarNum, "O") & String(10 - BarNum, "-")
    Me.Show vbModeless
End Sub

Public Function Update(Counter) As Boolean
    Dim Percent As Double, BarNum As Integer
    If Me.Visible Then
        iCon = (iCon Mod 3) + 1
        Percent = Counter / Full
        BarNum = Fix(Percent * 10)
        Me.LblTxt = Format(Percent, "000%") & " 終了 " & Chara(iCon)
        Me.LblTime = String(BarNum, "O") & String(10 - BarNum, "-")
        DoEvents
        If Counter >= Full Then Me.Btn.Caption = "終了"
        Update = False
    Else
        Update = True
    End If
End Function
ユーザーフォーム内にBtnという名前のボタン、LblTxt、LblTimeという名のラベルがあれば、上のプログラムは動きます。 このユーザーフォームはBtnボタンを押すとフォームが非表示になり、Updateプロシージャを呼ぶとTrueを返すようになっています。実際の使用方法は次のとおりです。
Sub test()
    Dim i As Integer, j As Long
    Const Max As Integer = 500
    TimerForm.Start Max
    For i = 0 To Max
        For j = 0 To 50
        If TimerForm.Update(i) Then GoTo Finish
        Next j
    Next i
Finish:
End Sub
Startで最終の数値を指定してやり、ループごとにUpdateを呼んでやることによって、進行度のダイアログを変更していきます。処理の長くなるループを行うときに動いているかどうかわからなくなる不安を取り除き、途中で中止する手段も提供するために作りました。サンプルファイルをご覧ください。たとえ指定したカウンターの値が変化していなくても、アニメーションが続いていきます。このプログラムを入れることによって処理速度はもちろん遅くなりますので、程度を見て入れるようにしてください。


目次に戻る

Application.Volatile(2002/2/21)

自作関数をワークシートで使っている場合で、セルの内容が変わったのに計算結果が変化しないことがありませんか?この原因のひとつに関数で直接セルを参照していない場合があげられます。
具体的に言うと、関数の引数でセルrを指定している場合、

この場合に、セルr以外の参照セルの内容(Range(r)やr.Offset(1))が変化しても、再計算は行われません。関数の初期設定では引数に含まれるセルが変化したとき以外、再計算が行われません。
ここで、Application.Volatileという命令が必要になってきます。この命令は「シートの中身が変わったら再計算しろ」というものです。この場合、どこのシートのどのセルが変化を受けても、一回の入力につき一回命令が実行されます。次のプログラムで確認してみてください。
Function test(r As Range)   'どのセルが変化を受けても1ずつセルの内容が増えていきます。
    Static i As Integer
    i = i + 1
    test = r + i
    Application.Volatile
End Function
再計算の問題が解決する命令ですが、ただいろいろな関数につければ良いというのではなく、処理速度にかかわってくるので、必要な関数にのみ記述するようにしましょう。


目次に戻る

イベントの停止(2002/3/9)

イベント(ユーザーの操作など状況の変化に反応するマクロ)を記述している場合、「今だけ動くな」ということがあります。例えば、次の場合です。

  1. 選択範囲の変化に対応するイベント(SelectionChangeイベント)内でセルを移動させる場合
  2. セル内容の変化に対応するイベント(Changeイベント)内でセルの内容を変更する場合
  3. イベントを実行させずにマクロを実行したい場合
  4. イベントが記述されているシートの内容を変更したい場合
1,2の場合は放っておくと無限ループに陥りますので、対処が必要です。そこで、EnableEventsプロパティが必要となります。

プログラム(シートのコードとして記入)

Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    Application.EnableEvents = False
    '操作の記述
    Application.EnableEvents = True
End Sub

EnableEventsプロパティにFalseが設定されている間はイベントの処理が行われません。3の場合も同様にイベントにかかわる命令の前後をEnableEventsプロパティで挟んでください。

4の場合ですが、良く、「SelectionChangeイベントで選択が拒否されているセルの内容を変更したい」という場合があります。このような場合は次のようにするとうまくいきます。
  1. Visual Basic Editorの画面で「中断(‖)」ボタンを押すと、イベントを含むマクロが停止する。
  2. シート内を変更する。
  3. Visual Basic Editorの画面で「実行(▲)」あるいは「リセット(■)」ボタンを押すと、イベントを含むマクロが再開する。
ただし、リセットボタンで再開すると変数の値等が初期化されますので注意してください。


目次に戻る

ワークシートとの値のやり取り(2002/3/15)

ユーザー(ワークシート側)とVBAの間でデータをやり取りする際、その値はどこに保存しますか?

となるでしょう。変数の方はVeryHiddenワークシートを作成して、そこに変数の値を保存することによって、短所の一つを克服することができます。デフォルトパラメータの設定や暗号等に使えるでしょう。しかし、一般的にはセルでのやり取りが圧倒的に多いのではないのでしょうか。

ところで、値のやり取り、主に恒常的なパラメータや定数にわざわざ、セルを使うのが見栄えが悪いと思っている方。もう一つ、やり取りをする場所があります。それは、名前(Name)です。以前、名前に式が使えるということを紹介しましたが、その応用です。
名前の定義ダイアログで「参照範囲」という欄がありますが、ここに式、あるいは数字、文字列を入力することができます。(数字や文字列のみ入力しても自動的に=が付加されます。)例えば、YenToDollarという名前を=120で定義することができます。ワークシートのセルからは=YenToDollarという形で参照できますし、VBAからは以下のような手順で、読み書きすることができます。

プログラム

Const TestName As String = "Test"

Sub NameRead()
    MsgBox Evaluate(Names(TestName).Value)	'読み込み
End Sub

Sub NameWrite()
    Dim i As Integer
    i = 5
    On Error Resume Next
    Err.Clear
    Names(TestName).Value = i	'書き込み
    If Err.Number <> 0 Then		'名前がない場合
        Names.Add Name:=TestName, RefersToR1C1:="=" & i
    End If
    On Error GoTo 0
End Sub

長所・短所はセルとあまり変わりません。セルを使わない変わりに、若干、入出力がしにくくなっています。興味のある方、試してみてください。


目次に戻る

Runメソッド(2002/4/13)

ApplicationオブジェクトにはRunメソッドというものがあります。これはマクロを実行させるメソッドで、文字列で実行するマクロを指定します。通常のマクロ・関数の呼び出しと比べて以下のような利点があります。

  1. プログラムを走らせている時点で(動的に)呼び出す関数を決定することができる。
  2. 外部プロシージャ(別のワークブック)の関数を呼び出すことができる。
若干制限はありますが、引数や帰り値も普通の関数と同様に使えます。
欠点としては、実行速度が遅いことが挙げられます。

プログラム

Sub RunMyMacro(num As Integer)
    Application.Run Range("MyMacroList").Cells(num).Value
End Sub

この例ではMyMacroListという名前のセル範囲にマクロ名を記述することによって、番号でそのマクロを実行することが可能となります。

プログラム2

Function RunTest(v As Variant)
    On Error Resume Next
    RunTest = Application.Run("RunTest" & TypeName(v), v)
    If Err.Number <> 0 Then '例外処理
        RunTest ="規定外:" & TypeName(v)
    End If
    On Error GoTo 0
End Function

Function RunTestInteger(i As Integer) As String
    RunTestInteger = "Integer: " & i
End Function

Function RunTestDouble(d As Double) As String
    RunTestDouble = "Double: " & d
End Function

Function RunTestRange(r As Range) As String
    RunTestRange = "Range: " & r.Address(False, False)
End Function

この例では引数の型によって呼び出す関数を変化させるということをしています。
もっともselect caseのほうがスマートですが。対応する変数型を増やしても元の関数に変化がいらないメリットはあります。


目次に戻る

仮想セル(2002/4/20)

プログラム

計算パネルプログラムでセルを使わずにワークシート計算を実現しています。これはどのようにやっているかと言うと、Evaluate関数に文字列をほりこんでいるだけと言うたわいもないものです。サンプルを見てください。
Sub VirtualCell()
    Dim VCell As String
    Dim Result As Variant
    VCell = InputBox("式を入れてください。")
    Result = Evaluate(VCell)
    If Not IsError(Result) Then
        MsgBox Result
    Else
        MsgBox "エラー: " & CInt(Result)
    End If
End Sub

ワークシート関数も問題なく使えます。Evaluate関数のHelpにはいろいろややこしいことが書いてありますが、「文字飾りや大きさのないセル」として使うことができると言うことです。


目次に戻る

覚えにくい関数をまとめてみよう(2002/6/1)

私は仕事柄、指数近似をよく使います。いつもはグラフに近似式を表示して傾きなどの値を読み取っているのですが、この値を別の式に代入してリアルタイムに変化させたい場合、いちいちグラフから読むわけにいきません。そこで、関数を使うのですが、例えばXの範囲がA1:C1,Yの範囲がA2:C2の以下のような場合、

ABC
1012
21005025

切片を求めようとすると次のようになります。

=Exp(Index(LinEst(Ln(A2:C2), A1:C1, True, True), 1, 2))

私の弱い頭では覚えてられませんし、xとyを別々に範囲指定がめんどくさく、しかもyを先に記述することに戸惑いを覚えます。理想とすれば、ほにゃらら(A1:C2)なのですが…
ここで、VBAの登場です。プログラムの汎用性のためにデータは、
  1. 縦型でも横型でもOK
  2. xとyの範囲がくっついていても離れていてもOK
  3. 傾き、切片、r2を求めたい
ということを念頭においてプログラムをしました。

プログラム

Public Function 指数近似(mode As String, x As Range, Optional y As Range = Nothing) As Double
    If データセット(x, y) Then
        With Application.WorksheetFunction
            Select Case mode
            Case "傾き"
                指数近似 = Evaluate("=Index(LinEst(Ln(" & y.Address & "), " & x.Address & ", True, True), 1, 1)")
            Case "切片"
                指数近似 = Evaluate("=Exp(Index(LinEst(Ln(" & y.Address & "), " & x.Address & ", True, True), 1, 2))")
            Case "r2", "R2", "相関係数"
                指数近似 = Evaluate("=Index(LinEst(Ln(" & y.Address & "), " & x.Address & ", True, True), 3, 1)")
            End Select
        End With
    Else
        指数近似 = CVErr(11)
    End If
End Function

Private Function データセット(x As Range, y As Range) As Boolean
    If y Is Nothing Then
        If x.Rows.Count = 2 Then
            Set y = x.Rows(2)
            Set x = x.Rows(1)
        ElseIf x.Columns.Count = 2 Then
            Set y = x.Columns(2)
            Set x = x.Columns(1)
        Else
            データセット = False
            Exit Function
        End If
    End If
    データセット = True
End Function

いろいろきれいな関数を考えたのですが、結局Evaluateにほりこむだけになっています(笑)。皆さんもご自分の使う関数を自作関数で簡単にしてはどうですか?


目次に戻る

覚えにくい関数をまとめてみよう(その2)(2002/6/6)

前回のおさらいです。物が一定の時間にある割合で減っていく様子は、次のような式で表されます。

    % = Ae-kt
[%]は一定時間(t)過ぎた後の量で[A]は最初の量,[k]は減るスピードです。ある時間における残っている量を何点かグラフに書いていくと指数関数に近くなり、近似的に[A],[k]を求めることが出来ます。その求める式が次のとおりでした。
[A]: =Exp(Index(LinEst(Ln(A2:C2), A1:C1, True, True), 1, 2))
[k]: =Index(LinEst(Ln(A2:C2), A1:C1, True, True), 1, 1)
前回のプログラムはこれを求めるのに式を羅列していましたが、やはり美しくありません。メンテナンスも大変です。また、欲深く、当てはめた近似式からある時間(t)における割合(%)の予想や逆にある一定の割合に減るのにかかる時間が求まらないか?また、ある一点だけ代入して(0時間の量を100として)、式を求めることも出来ないか?といろいろ考えました。
そこで、次のようなプログラムになります。

プログラム

Public Function DegFunc(mode As String, x As Range, Optional y As Range = Nothing, Optional v As Variant) As Double
    Dim k As Double, A As Double, R2 As Double
    Dim RA As Variant
    Const DefY As Double = 100
    If DegFunc_Separation(x, y) Then
        If x.Cells.Count = 1 And y.Cells.Count = 1 Then
            RA = Evaluate("LinEst(Ln({" & DefY & "," & y & "}), {0," & x & "}, True, True)")
        Else
            RA = Evaluate("LinEst(Ln(" & y.Address & "), " & x.Address & ", True, True)")
        End If
        
        k = -RA(LBound(RA), LBound(RA))
        A = Exp(RA(LBound(RA), LBound(RA) + 1))
        R2 = RA(LBound(RA) + 2, LBound(RA))
        
        With Application.WorksheetFunction
            Select Case mode
            Case "k":               DegFunc = k                     '分解速度
            Case "A":               DegFunc = A                     'Y切片
            Case "r2", "R2":        DegFunc = R2                    '相関係数(決定係数)
            Case "DT50", "T1/2":    DegFunc = Log(2) / k            '半減期(50%になるまでの期間)
            Case "DT90":            DegFunc = Log(10) / k           '90%になるまでの期間
            Case "t":               DegFunc = Log(A / CDbl(v)) / k  '一定の割合になるまでの期間
            Case "%":               DegFunc = A * Exp(-k * CDbl(v)) '一定期間後の割合
            End Select
        End With
    Else
        DegFunc = CVErr(11)
    End If
End Function

Private Function DegFunc_Separation(x As Range, Optional y As Range) As Boolean
    If y Is Nothing Then
        If x.Rows.Count = 2 Then
            Set y = x.Rows(2)
            Set x = x.Rows(1)
        ElseIf x.Columns.Count = 2 Then
            Set y = x.Columns(2)
            Set x = x.Columns(1)
        Else
            DegFunc_Separation = False
            Exit Function
        End If
    End If
    DegFunc_Separation = True
End Function

呼び出しているサブルーチンは名前が変わりましたが、中身は変わっていません。
行数は増えましたが、すっきりしたと思います。計算で得られた結果を配列に代入してから、VBAの作法で取り出しています。またいったん変数に代入してやることによって、再利用を容易にしています。
WorksheetFunctionが使えないので、Evaluateを使っているのが見苦しいですが、私の力では仕方ありません。うまい方法を知っている方、教えてください。


目次に戻る

覚えにくい関数をまとめてみよう(その3)(2002/6/13)

さて、前回まで自作関数を作ってきました。この関数をほかのワークブックで使うにはどうしたらいいでしょう?

一つには標準モジュールをそのままコピーすることが考えられます。この方法は最も手軽ですが、使いたいワークブックに全部コピーするのはめんどくさいですよね。他には参照設定をするという方法があります。こちらは、以前紹介しています。ただ、こちらも使いたいワークブック全てで設定する必要があります。

今回は「アドイン」にしてしまうという方法を紹介します。実は前から、SuperSubのファイルに書いてあるのですが。
そのまま引用してみます。

常に使う予定であれば、「名前を付けて保存」の「ファイルの種類」で
「Microsoft Excel アドイン」(一番下です)を選択して保存し、
Excelを再起動した後、「ツール」→「アドイン」でSupersubにチェックを入れてください。
次回から、Excelを立ち上げるたびに自動的に起動されます。
(マクロの警告パネルも出なくなります。)
アドインにする利点は「関数が常に利用できる」「マクロチェックをしなくなる」という点が上げられます。欠点としては「ワークシートを見ることが出来なくなる。」「配布には向かない(各コンピュータでアドインを設定する必要がある)」という点が上げられます。


目次に戻る

数式でセル参照をしてる?(2002/10/11)

数式でセル参照をしているかどうかを調べる条件式を考えてみました。

プログラム

If ActiveCell.Formula <> ActiveCell.FormulaR1C1 Then
	MsgBox "数式内にセル参照があります。"
End If

セルの参照形式によって数式が異なることに着目しています。


目次に戻る

にゃま夫のへや   ・Office VBA   ・おもちゃ箱   ・iαppli
ホームページに戻る。
無断転載を禁じます。
にゃま夫