【化学系のVBA入門講義】第四講:関数とは何か?仕組みと作り方を学ぶ

こんにちは、ツクダンです!

  • 関数の仕組み
  • オリジナル関数の作り方

今回の講義では、この二つのことについてVBA超初心者向けに解説します。

 

まずは第三講までのリンクを貼っておきますね。

タイトルを見て不安な部分がある人は、確かめておいてください。

第一講:開発タブの表示

第二講:VBEの使い方

第三講:変数の宣言とデータ型

 

今回の第四講のタイトルは

第四講:関数とは何か?関数の仕組みと作り方を学ぶ

です。

では始めましょう。

関数とは何か?

ではまず関数の正体を理解していきましょう。

結論から言うと、関数とはプロシージャです。

第二講でプロシージャとは、以下のようなSub~End Subに囲まれた「命令のカタマリ」だと説明しました。

Sub 初めての実行()
    MsgBox "Hello World!"
End Sub

すみません。実はこれは少し嘘を言っていました。

本来Sub~End Subで囲まれた命令はSubプロシージャと呼ばれ、三種類あるプロシージャの一つにすぎません。

そして関数は、三種類のプロシージャの中のFunctionプロシージャに当たります。

Subプロシージャとは別物です。

ここでは、関数がFunctionプロシージャであるということを理解するために、次のトピックを深堀りしていきましょう。

  • 関数の特徴
  • 三種類のプロシージャの特徴

 

 

関数の特徴

まずは関数の特徴を考えていきます。

具体例として基本的なExcel関数をいくつか考えましょう。

 

SUM関数

SUM関数は「合計値」を計算してくれる関数。

特徴は次の二点があげられます。

  • SUM(引数1,引数2,・・)の形をとる
  • 各引数の値を合計した値がセルに返ってくる

 

 

 

AVERAGE関数

AVERAGE関数は「平均値」を計算してくれる関数。

特徴は次の二点があげられます。

  • AVERAGE(引数1,引数2,・・)の形をとる
  • 各引数の値を平均した値がセルに返ってくる

 

 

 

関数の特徴とは

つまり関数の特徴とは、以下の二つがあげられるわけです。

  • 関数名(引数1,引数2,・・)の形
  • 特定の処理がされた値が返ってくる

 

 

 

3種類のプロシージャ

つづいてプロシージャの特徴について説明していきます。

プロシージャの役割

プロシージャについて細かく学ぶ前に、プロシージャの役割について理解しておく必要があります。

プロシージャの役割の神髄は、「何らかの処理を加える」という部分です。

入力された値に対して、合計や平均といったような処理を行えることこそ、プロシージャの役割の本質を掴んだ部分になります。

 

そして処理された値を「どのように出力するか」の違いで、プロシージャには以下の三つの種類に分かれます。

  • Subプロシージャ
  • Functionプロシージャ
  • Propertyプロシージャ

 

Subプロシージャ

Subプロシージャは、戻り値の不要な手続きを実行するためのプロシージャです。

「マクロの記録」で作った「マクロ」はSubプロシージャで作られます。

 

「戻り値」とは、処理を行ったあと関数に返ってくる値です。

例えばSUM関数やAVERAGE関数の場合を考えれば分かりやすいでしょう。

SUM関数・AVERAGE関数では、入力された値に合計・平均の処理をした後、その処理後の値がセルに入力されます。

これは「処理された値」が「関数」に返ってきている状態なのです。

この値を「戻り値」と言います。

 

「処理された値が返ってくる」のが関数の特徴なので、Subプロシージャはこれに当てはまらないのですね。

 

Functionプロシージャ

Functionプロシージャは、手続きを実行した結果として戻り値を返すプロシージャです。

これです。関数はこれなのです。

Subプロシージャとの違いは、「戻り値を返すか否か」の部分だけです。

 

Propertyプロシージャ

Propertyプロシージャは、プロパティの作成や操作を行うためのプロシージャです。

ここでPropertyプロシージャをきちんと説明してもいいのですが、やや高度な内容になります。

なのでこの講義での説明は、差し控えることにしました。

第6講あたりで「オブジェクトのプロパティとメソッド」についての講義をしようと思うので、その時に説明します。

 

 

 

関数の作り方

続いては関数の作り方についてみていきましょう。

とは言っても関数はプロシージャですので、プロシージャの作り方を説明します。(Propertyプロシージャは割愛)

ちなみにプロシージャを「作る」ことを「定義する」と表現するのが一般的なので、以降はそのように記述していきます。

 

Subプロシージャを定義する

Subプロシージャを定義するには、次のようなSubステートメントを使用します。

Sub プロシージャ名([引数リスト])
    <処理内容>
End Sub

これだけです。簡単ですね。

ただ「引数リスト」に関しては、二つほどポイントがあります。

  • 引数リストは省略可能である
  • 記述する場合は、記述ルールがある

引数の記述ルールに関しては、またあとから説明します。

 

Functionプロシージャを定義する

Functionプロシージャを定義するには、以下のFunctionステートメントを使用します。

Function プロシージャ名([引数リスト]) [As データ型]
    <処理内容>
    プロシージャ名 = 処理後の値 
End Function

Subステートメントと比較して、ポイントをいくつかあげてみましょう。

  • 引数リストは省略可能である
  • [As データ型]で戻り値のデータ型を指定できる(省略可)
  • 「プロシージャ名 = 処理後の値」で戻り値を指定

戻り値を設定する分、Subステートメントより少し複雑ですね。

ちなみにAs Typeを省略した場合、データ型はVariant型になります。

 

引数のルール

続いて[引数リスト]の中身の記述ルールについて解説していきます。

まずは引数リストの記述の中で指定することが可能な項目を表にまとめます。

そしてそれぞれを次のような順で記述します。

以下、この書き方を「引数の構文」と呼びます。

[Optional] [ByVal or ByRef] パラメーター名 [As データ型] [= デフォルト値]

詳しくは後述しますが、まずは引数の構文のポイントをまとめてみましょう。

  • パラメータ名以外はすべて省略可能である
  • ByValとByRefはどちらか一方を指定できる(省略するとByRef)
  • 関数に入力する引数と、プロシージャ内のパラメータの数は同じ
  • データ型を省略するとVariant型になる

 

引数とパラメーターの違い

わりと混同しがちですが、引数とパラメーターは全くの別物です。

それぞれを簡単に説明すると、次のようになります。

引数:実際に関数に入力する値

パラメーター:引数に入力された値を受け取るための仮置きの変数

 

これを理解するには、関数側とプロシージャ側の視点を分けて考える良いでしょう。

関数とプロシージャは本質的には同じものですが、それらは表裏一体の関係にあります。

「表のExcelでは関数と引数しか見えませんが、その裏側のVBEではプロシージャとパラメータが動いている」といった感覚です。

 

 

ByVal(値渡し)とByRef(参照渡し)の違い

関数を使ってプロシージャを呼び出す際の引数の渡し方には、値渡し参照渡しの二種類があります。

ここではそれらの違いについて、分かりやすく解説していきます。

 

まずは値渡しで定義されたSubプロシージャValueと、参照渡しで定義されたSubプロシージャReferenceをそれぞれ実行してみてください。

値渡し>

Sub Value()
    
    Dim x As Long: x = 10 'xを定義して10を代入
    Call Increment(x)  'Incrementプロシージャを呼び出す
    Debug.Print x
    
End Sub
Sub Increment(ByVal num As Long)

    num = num + 1  '入力された値に1を足して、もとの値を上書きする処理

End Sub

 

<参照渡し>

Sub Reference()
    
    Dim x As Long: x = 10 'xを定義して10を代入
    Call Increment(x)  'Incrementプロシージャを呼び出す
    Debug.Print x
Sub Increment(ByRef num As Long)

    num = num + 1  '入力された値に1を足して、もとの値を上書きする処理

End Sub

 

SubプロシージャIncrementは「入力された値に1を足して、もとの値に上書きする」という処理を行うもの。

プロシージャValueとプロシージャReferenceの違いは、引数のパラメーターへの受け渡しが「値渡し(ByVal)」か「参照渡し(ByRef)」かの違いのみです。

しかしイミディエイトウインドウを確認すると、SubプロシージャValueではIncrement関数の実行後もx=10のまま変わっていません。

 

これはパラメーターnumに代入されるものが、プロシージャValueとプロシージャReferenceで異なることが原因です。

  • 値渡し:変数xの中の値を複製しnumに代入
  • 参照渡し:変数xのアドレスをnumに代入

 

「値渡し」では見た目が同じの別の値に対して、SubプロシージャIncrementの処理が行われます。

処理はIncrement内で完結するので、処理後の値はSubプロシージャValueには反映されません。

コピーされたExcelファイルの中で作業しても、コピー元のExcelファイルに影響がないのと同じです。

 

一方「参照渡し」ではnumに代入されたアドレスの先にある値に対して、SubプロシージャIncrementの処理が行われます。

間接的ではありますが、xの中の値そのものにIncrementの処理が行われるので、SubプロシージャRefrenceに処理結果が反映されます。

デスクトップにショートカット設定したExcelファイルで作業を行うのと同じ仕組みです。

作業自体は、ショートカット先の元ファイルに反映されますよね。

 

 

Optionalキーワードとデフォルト値

Optionalキーワードを用いると、その引数を省略可能にすることができます。

これを「引数をオプションにする」と表現します。

この場合、引数が実際に省略されたときに備えてデフォルト値を設定する必要があります。

記述のポイントは二つ。

  • Optionalキーワードは引数の構文の一番最初
  • デフォルト値は「=デフォルト値」の形で一番最後

 

Sub オプション()
    
    Call SayHello("Hello", "Tom")
    Call SayHello("GoodBye")  '第二引数を省略
    
End Sub
Sub SayHello(message As String, Optional name As String = "Bob")

    MsgBox message & ", " & name & "!"  '挨拶+名前をメッセージで出力

End Sub

なお、Optionalキーワードを使用する際のルールとして、引数をオプションにした場合、その引数以降に定義する引数もすべてオプションにしなければなりません。

Optionにする引数は、出来るだけ最後の方に定義する方が良いでしょう。

 

 

Functionプロシージャ(関数)を作ってみる

ここまで学んだ知識を使って、「自己紹介メッセージ」を戻り値とする関数を作成してみましょう。

以下のSubプロシージャ自己紹介を実行することで、

「初めまして、○○(名前)と言います。」

「来年、~歳になります。」

とメッセージ出力できるような、NiceToMeetYou関数を作成してみてください。

このSubプロシージャはコピペしてください。

 

Sub 自己紹介()

    Dim age As Long, aisatsu As String
    age = Application.InputBox("現在の年齢を入力してください", Type:=1)
    
    aisatsu = NiceToMeetYou(age, "名前")
    
    MsgBox aisatsu
    
End Sub

 

ただし以下の二つのルールを付け加えます。

  • 名前の引数のデフォルト値を、「名無し」に設定する。
  • 入力した年齢+1の年齢が出力されるようにする。

 

以下に解答例を示しますので、見たくない人はここでストップしてください。

Function NiceToMeetYou(ByRef NowAge As Long, Optional name As String = "名無し")
    
    Dim Hello As String, NextAge As Long
    
    Hello = "はじめまして、" & name & "と言います。"
    NextAge = NowAge + 1
    
    NiceToMeetYou = Hello & "来年" & NextAge & "歳になります。"

End Function

 

多分、難しかったですね。

今はできなくていいので、感覚だけ掴んでおいてください。

 

 

関数(プロシージャ)を呼び出す

最期に、関数(プロシージャ)を呼び出しについて説明していきます。

これまでしれっと呼び出してきましたが、実はいくつかルールがあります。

ポイントは二つ。

  • Callステートメントの使用
  • 丸括弧()の有無

 

ただSubプロシージャの場合とFunctionプロシージャの場合で、ルールが異なります。

なのでそれぞれで分けて説明します。

 

Subプロシージャ

Subプロシージャを呼び出すときには、Callステートメントを使います。

記述方法は以下の通り。

Call プロシージャ名 (引数リスト)

ポイントは二つ。

  • Callステートメントは省略可能
  • 省略した場合、引数リストの丸括弧を書かない

具体的な例を見てみましょう。

 

Sub callあり()
    
   Call SayHello("Bob")
    
End Sub


Sub call無し()
    
    SayHello "Bob"
    
End Sub
Sub SayHello(ByVal name As String)
    
    MsgBox "Hello," & name & "!"
    
End Sub

Call有りでも無しでも、全く同じ出力になります。

 

 

Functionプロシージャ

Functionプロシージャを呼び出す場合、戻り値を使用するかどうかで記述方法が異なります。

 

戻り値を使用しない

戻り値を使用しない場合の記述方法は以下の通り。

Call プロシージャ名 引数リスト

ポイントは二つです。

  • Callステートメントは省略可能
  • 引数に丸括弧()をつけない

 

戻り値を使用する

戻り値を使用する場合は、以下のように記述します。

プロシージャ名 (引数リスト)

引数リストに丸括弧()を必ずつけることを忘れないように。

 

Sub 戻り値を破棄()
    
    Dim x As Long: x = 100
    GetTax x
    
End Sub

Sub 戻り値を使用()
    
    Dim x As Long: x = 100
    Debug.Print GetTax(x)
    
End Sub
Function GetTax(ByVal price As Long) As Currency
    
    Const Tax_Rate As Currency = 0.1
    GetTax = price * (1 + Tax_Rate)
    MsgBox GetTax
    
End Function

どの記述方法を使うべきか

Callステートメントと丸括弧()の組み合わせ。

選択肢がありすぎてどれを使うべきか判断できないという人もいるでしょう。

 

そんなときの基本方針は「読みやすいコード」を目指すこと。

紛らわしい記述を避けるような方針をとればいいのです。

 

その視点から見ると、今回の問題は

  • Call省略のSubプロシージャ
  • 戻り値を使わないFunctionプロシージャ

の記述がかぶっていること。

 

それを解決するために次の記述方法で統一しましょう。

  • Subプロシージャは必ずCallで呼び出す
  • Functionプロシージャは()の有無のみ変える
  • FunctionプロシージャはCallを使用しない

 

 

まとめ:関数の仕組みと作り方

お疲れさまでした。

第四講では、ExcelやVBAで用いられる関数の仕組み、その作り方について学んできました。

ポイントは以下の通り。

  • 関数とはFunctionプロシージャである
  • 引数の構文には、様々なルールがある
  • 関数の呼び出しはCallの使用と戻り値の使用がポイント
  • 記述方法に迷ったときは、読みやすいコードを目指す

 

本講義の内容は難しい部分も多かったと思います。

ただ非常に本質的で重要な部分です。

しっかり復習して、是非とも理解して欲しいです。

といっても2,3回読んでも分からないときは、いったん飛ばしてもいい。

学習を進めていくと、ふとわかるときが来ますから。

ゆったり行きましょう。

では次は第五講で。

おつかれさま。

最新情報をチェックしよう!