OriosのActiveBasic講座(1-3):プロシージャ

2.Functionブロック

【目標】「Function」を使ってプログラムをまとまった効率のよいものにできるようにしましょう。

今回は「Functionブロック」を学習します。 これは前回のSubブロックと非常に似ていますがSubブロックより頻繁に使われます。 実はプログラミングにおいてFunctionブロックの内容を理解できないとプログラミングが非常に困難になります。 ということで今回はその重要な「Functionブロック」を学習します。

'lesson1-3-2
#console
Dim date As Long
Dim rel As Long
Dim b_a As Long
Input "日を入力してください。", date
rel = RelationTo14(b_a)
If b_a = 1 Then
Print "14日から"; rel; "日経過しました。"
Else
Print "14日まであと"; rel; "日です。"
End If
Sleep(3000)
End

Function RelationTo14 (ByRef b_a As Long) As Long
If date < 0 Then
RelationTo14 = -1
Exit Function
End If
RelationTo14 = 14 - date
If RelationTo14 < 0 Then
RelationTo14 = - RelationTo14
b_a = 1
End If
End Function

C:\ActiveBasicCourse\lesson1-3-2...
日を入力してください。13
14日まであと 1 日です。

今回は今までに比べて長めのソースですがいかがでしょうか? 今回は入力された日がその月の14日と差がどのくらいあるかを計算するプログラムです。 ところで14日といえば管理人に縁なしの"St.Valentine's Day"なる日が2月にあったような……。 そんなことは放っておいて今回の解説です。 「14日」に惑わされずに集中して読みましょう。

-*-*-*-*-説明-*-*-*-*-

【使い方】
Function プロシージャ名 (引数の種類 引数名 As 引数の型 ) As 戻り値の型  command  EndFunction

【備考】
(1) "戻り値の型 " と"command "、"command "と"EndFunction"の間には改行を入れます。
(2)「引数の種類 引数名 As 引数の型 」のかたまりは、「,」で区切ることによって括弧内に何個でも作れます。 またこのかたまりを書かずに"()"という風にすることも可能です。
(3)EndFunctionの間は空けても空けなくてもOKです。

【意味】
引数を引数の種類 ・引数の型 である引数名で受け取り(複数可)、command を実行し、型が戻り値の型 である戻り値を持つプロシージャ名 という関数を作ります。

【解説】
(1)概説
皆さんは「関数」という言葉を聞いたことがありますでしょうか? 「関数」というのはある値(複数可)が与えられた場合にその値に応じた1つの値を返すというものです。 例えば与えられた数の2倍に1を足した値を返す関数があったとします。 するとこの関数に「1」を与えれば「3」(=2×1+1)が、「12」を与えれば「25」(=2×12+1)が返ってきます。 このような「関数」というものを作るのがこの「Functionブロック」です。

(2)ブロックの作り方
Functionブロックの置き方・名前の付け方・値の渡し(与え)方は前回やったSubブロックと同じです。 自信のない方はもう一度見ておいてください。
で、問題なのは「Functionブロック」特有の「戻り値」です。 置き方・名前の付け方・値の渡し方など「Subブロック」と似ている部分が多いのですが、この「戻り値」だけは「Functionブロック」特有のものです。 まず「戻り値」の型を設定してやります。 これは戻り値の型 のところにLongやDoubleなどの型の名前を書いてやればOKです。 次に実際に戻り値を設定する方法ですが、これはそのブロック内でプロシージャ名 をあたかも変数のように扱ってやることで戻り値の設定ができます。 例えば"Function a(……"という関数の戻り値を"2*x+1"にしたければ、このブロック内で"a=2*x+1"としてやるだけでOKです。 また戻り値はブロック内での最終的なプロシージャ名 の値になります。 つまりさっきの例では"a=2*x (改行) a=a+1"という書き方もできるということです。



【使い方】
ByRef 引数名  As 引数の型 

【備考】
これは「Functionブロック」の宣言部中の「引数の種類 引数名 As 引数の型 」の部分にこれを当てはめます。

【意味】
引数名 を参照型で引数の型 として受け取ります。

【解説】
(1)概要
前回の「(1)-(4)引数」の部分で「引数の種類……『ByVal』と『ByRef』の2つがあります。」と書いたのを覚えているでしょうか? ここではその「ByVal」と「ByRef」の違いについて説明します。

(2)変数の仕組み
本題に入る前にコンピューター上での変数の取り扱いについて少し知っておいてもらいたいことがあります。 まず変数はどこに保存されていると思いますか? この質問には「常識だ」といって即答する人もいらっしゃるでしょうし、また「そんなの分かんな〜い」と思う人もいらっしゃるかもしれません。 答えは「メモリー」です。 さて、次の問題です。実際にコンピューター上でプログラムを実行させるとき、変数を使うには何が必要でしょうか? これは少し難しい上に表現も分かりにくいと思いますが答えは2つあります。 1つは「値」です。これがないと変数の意味がありません。 ではもう一つは何でしょうか? 実際に変数を保存するときのことを考えてみましょう。 最近のメモリーはだいたい512KBの記憶領域があります。 これはLong型変数を約13万個も保存できる量です。 この中に変数の値を適当な場所に保存していいでしょうか? ダメですね。そんなことをしたらどこにあるか見つけられません。 ここまで来ればかなりの人がピンと来るのではないでしょうか? そうです。変数をどこに保存しているかを示す「住所」が必要になります。 それをコンピューター用語では「アドレス」といい、そのアドレスを保存しているものを「ポインタ」といいます。
つまり変数を使用するには「ポインタ」と「値」が必要になるということです。

(3)ByValを使って宣言された引数について
これは別名「値型」と呼ばれており、渡された引数の「値」を宣言されている変数に代入します。 例えば"ByVal a As Long"と宣言されており、ここに値が「30」の変数bを渡した場合、aにはbの値である「30」だけをコピーされます。 つまりaをbの「そっくりさん」にするわけです。 なのでaがその後FunctionやSubブロック内でどんな値に変わろうともbには影響ありません。 Aさんのそっくりさんが例え整形手術をしてもAさんには何の影響もありませんでしょ?

(4)ByRefを使って宣言された引数について
これは別名「参照型」と呼ばれており、渡された引数の「ポインタ」を「参照」し、その引数のアドレスを宣言された変数のポインタ(その変数の住所を示すもの)に代入します。 例えば"ByVal a As Long"と宣言されており、ここにアドレスが「100」の変数bを渡した場合、aのポインタにはbのアドレスである「100」がコピーされます。 すると変数aのポインタは変数bのポインタをコピーしたため、変数aと変数bのアドレスが同じになり、コンピューター上では変数aと変数bが同じものとして扱われるようになります。 そのため変数aの値はbの値になりますし、また変数aの値を変更すると、bの値も変更されてします。 つまりaとbは「運命共同体」になるわけです。



【使い方】
変数 = プロシージャ名 (引数 )

【備考】
引数が複数必要な場合は「,」で区切って必要な数だけ書きます。また引数がいらない場合は引数を書かずに"()"のようにします。

【意味】
プロシージャ名 を呼び出し、戻り値を変数 に代入します。

【解説】
(1)呼び出し方
呼び出し方・引数を渡す際の注意はSubブロックのときと同じです。 自信がない方は見直しておいてください。

(2)戻り値の受け取り
戻り値の受け取り方ですが、戻り値を代入したい変数の後に"=プロシージャ名 (引数 )"を書いてやることで戻り値を代入させることができます。 注意する点は変数 とプロシージャ名 の型を合わせてください。 これはプロシージャに引数を渡すときと同じです。 戻り値が数値なのに文字列(String)型の変数で受けたり、またその逆や、さらにはまた数値の場合はサブルーチンを準備している変数の型の範囲よりも大きい数値を渡すようなことはできません。



【解説】
(1)前回プロシージャ内でDimを用いて宣言した変数はローカル変数として扱われることを学習しました。 今回は「グローバル変数」について学習します。

(2)プロシージャ内でDimを用いて宣言した変数がローカル変数として扱われるのに対し、プロシージャDimを用いて宣言した変数は「グローバル変数」として扱われます。 グローバル変数は、宣言したプロシージャ内でしか使用できないローカル変数と違い、プロシージャの中だろうと外だろうとどこでも使うことができます。 またプロシージャ内でグローバル変数の値を変えた場合、その変更はプロシージャを抜けても破棄されることなく、プロシージャの外に影響を及ぼします。

(3)ただし、前回も書きましたように、プロシージャ内で宣言された変数(ローカル変数)が、プロシージャの外で宣言された変数(グローバル変数)と同じ名前の場合、ローカル変数の方として扱われます。 つまりこの2つの変数は名前こそ同じだけれども全く違う独立した変数として扱われるということです。 なのでローカル変数の初期値(何も代入していないときの値)が同名のグローバル変数の値になることはありませんし、ローカル変数の方の値を変えてもグローバル変数の値は変わりません。 またプロシージャを抜けると同時にローカル変数のほうが破棄され、プロシージャの外ではローカル変数として扱われなくなります。



【使い方】
Exit プロシージャの種類

【備考】
プロシージャの種類 はFunctionまたはSubです。

【意味】
現在のプロシージャの種類 を抜けます。

【解説】
(1)にループの抜け方として「Exit ループの名前 」を学習しましたが、プロシージャを抜けるにもExitを使うことができます。 Exitの後に、現在実行しているプロシージャの種類を書くことで、現在実行しているプロシージャを抜ける(途中でやめる)ことができます。

(2)なぜ「現在実行している」ということにこだわるかというと、プロシージャの中からさらにプロシージャ(同じもの・別のものともに)を呼び出すことができるからです。 例えば"Function a(x ..."と"Function b(x ..."という2つのプロシージャがあった場合、関数aの中で"a=b(x)+1"というような表現をすることもできます。 この場合、関数bの中でExit Functionを使うと「関数bだけ」を抜けます。 関数aが一緒に終了してしまうことはありません。



今回のソースについて

今回のソースを見ていただいたら分かるのですが、「b_a = 1」のように「=」と右・左辺の間に半角スペースが入っています。 これは半角スペースは語(文字・変数・数値・演算子など)と語の間にいくら入れても構わないからです(それでもあんまり入れすぎるとどうなるか分かりませんが……)。 なのでソースが詰まって見にくいなあと思うときは半角スペースを適度に入れて見やすくすることができます。


今回のFunctionはどうでしたか? 前回のSubと似た部分があったので前回よりは分かりやすかったですか? それとも本編の中で今までにないソースの長さで混乱してしまいましたか? どちらにしろプロシージャというのはプログラミングで重要な項目の1つです。 自分のペースでいいですから確実に理解できるように頑張ってください。 質問は掲示板で随時受付中です(といって来たのは1回だけだったような……)。 では今回の「やってみよう」です。

-*-*-*-*- -*-*-*-*-

ユーザーに2つの数字を入力してもらい(以下順にa,bと表記)、aをbで割ったときの商と余りを、そういう機能を持つ関数を作った上で求めるプログラムを作ってみよう。 ただし、関数の戻り値は商の方で、a,bはグローバル変数の方を使って計算し、割り算はForループを使って行い、そしてbが0または負の数のときは戻り値として「-1」を返すようにしてください。




ご質問・ご意見・ご感想・苦情等はメールまたはメイン掲示板のActiveBasic板などでお気軽にどうぞ。

前に戻る - 次に進む

ホーム > OriosのActiveBasic講座(トップページ) >