見出し画像

【学習ログ】Geminiと読み解く『パーフェクトExcel VBA』 #14|第2章 式と値⑫

前回はGeminiと一緒にエラー値の生成、エラー値の判定について、「何のために(目的)」と「どうやって(手段)」を確かめながら、学んでいきました。今回はそこでGeminiが具体例として生成してくれたコードを読み解いていきます。

まず自分で解釈して、Geminiに問い合わせていますが、問い合わせに関しては、基本的にあまりきれいな日本語ではなく、スピード重視なので、読みづらいのは悪しからず。わざわざこんがらがった私の頭の中を露呈するのは、「初学者の頭の中ってこんな感じ」を見てもらえたら、私と同じ初学者だけでなく、教える立場の方々にも何かのヒントになるかも、と思った次第です。

まずは、「割り算をする自作関数」
エラー値を「作る」:CVErr関数 の使用例です

Function MySafeDiv(a As Variant, b As Variant) As Variant
    ' もし分母が 0 だったら…
    If b = 0 Then
        ' VBAを止めずに、セルの表示を「#DIV/0!」にする
        MySafeDiv = CVErr(xlErrDiv0)
    Else
        ' 普通に計算する
        MySafeDiv = a / b
    End If
End Function

自分なりの解釈をして、Geminiに問い合わせます。

【具体例:割り算をする自作関数】は「割り算をする自作関数」を定義するコードを書いてくれたということだよね?この関数を使ったときに、もし分母に0が入ってたら計算しないで、「#DIV/0!」って表示する、そうじゃなかったら計算するっていう関数だよってことだよね?で、いまはaもbもとくに定義されてないから、このままじゃ使えない、ってことかな?これをSubプロシージャで呼び出して、計算するときに、Subのなかにa = 1 b = 2 とか入ってると機能するのかな?または入力を代入するみたいにしてabをきめるようにするのかな?そんなかんじであってる?

Geminiの回答はこちら。

「定義するコード」の意味: はい、あのコードは「こういう計算機を作りますよ」という**設計図(定義)**です。設計図を書いただけでは、まだ動きません。
a と b の状態: その通りです!この a と b は、「ここに数字が入る予定だよ」というただの空っぽの穴(プレースホルダー) です。まだ値は入っていません。
どうやって使う?: おっしゃる通り、Sub プロシージャなどから、「これを使って!」と呼び出すときに、初めて a=1、b=2 のように具体的な数字を渡します。

よかった。Geminiはこれを使うためのSubプロシージャも書いてくれました。

' ここで、さっき作った計算機(MySafeDiv)を使います!
Sub Test_Calculation()
    Dim result As Variant
    
    ' 【実験1】 成功パターン(10 ÷ 2)
    ' ※ aに10、bに2 を渡して呼び出す
    result = MySafeDiv(10, 2)
    Debug.Print "10÷2の結果: " & result
    
    ' 【実験2】 エラーパターン(10 ÷ 0)
    ' ※ aに10、bに0 を渡して呼び出す
    result = MySafeDiv(10, 0)
    
    ' 結果を確認してみる
    If IsError(result) Then
        Debug.Print "10÷0の結果: エラーが発生しました(" & CStr(result) & ")"
    Else
        Debug.Print "10÷0の結果: " & result
    End If
    
End Sub

なるほど、さっそく実行してみます。Geminiによると、一つのモジュールの中に両方のコードを入れても動くし、別々にしてもよいとのこと。一般的には分けることのほうが多く、技術者たちは、ユーザー定義関数を入れておく専用のモジュールに入れておき、様々なプロシージャから呼び出すようです。今回は練習用なので、一つのモジュールにまとめておくことにしました。

実行結果

実行結果はご覧の通り。
Debug.Print はイミディエイトウィンドウに表示されるのですね。

ここで、Geminiの作ってくれたコードの中のコメントに違和感が湧きました。再度問い合わせます。

さっき作ったfnMySafeDivの中で、
「' VBAを止めずに、セルの表示を「#DIV/0!」にする
        MySafeDiv = CVErr(xlErrDiv0)」
ここの部分て、「セルの表示を「#DIV/0!」にする」なの?
Subでセルへの出力がされている場合、セルへの表示が「#DIV/0!」になるのであって、やっていることは、 関数MySafeDivの動作に見える。動作の結果、#DIV/0!を持っている、というのかなあ、そんな感じに見える

日本語がおかしいですが、お気になさらず(笑)。
さて、Geminiの回答です。

その通りです!大正解!

私のコメント(' セルの表示を...にする)は、「最終的な結果としてどうなるか」 を書いただけの「意訳(ざっくり説明)」でした。

でも、あなたのコードの読み解き方のほうが、技術的には正確で、解像度が高い です。

あなたの解釈の正しさ:
「関数MySafeDivの動作に見える。動作の結果、#DIV/0!を持っている、というのかなあ」
まさにこれです。

この行:
fnMySafeDiv = CVErr(xlErrDiv0)

がやっていることは、 「セルに書き込む」ことではありません。
「関数の『戻り値(返事)』として、エラー値セットする」 という処理です。

図解:リレーで例えると

この処理の流れを「手紙のリレー」だと思ってください。
Function (fnMySafeDiv) の仕事
計算しようとしたけど無理だった。
だから、「答え」を書くための封筒(戻り値)の中に、「エラーだよ!」という手紙(CVErr)を入れた。
★ここでFunctionの仕事は終わり!
(まだセルには何も表示されていません)

呼び出し元 (Sub や Excelのセル) の仕事
Functionから封筒を受け取る。
中身を開けたら「エラーだよ!」という手紙が入っていた。
それを見て、セルに #DIV /0! と表示した。


つまり、この行の本当の意味は…
fnMySafeDiv = CVErr(xlErrDiv0)

これを厳密に翻訳すると、こうなります。
× (誤) 直接セルを書き換えて、表示を変える。
○ (正) 「私の計算結果は『エラー値』ですよ」という状態を**保持(セット)**して、呼び出し元に渡す準備をする。


セルに表示されるのは、あくまで 「呼び出し元がその結果を受け取って、セルに書いたから」 という結果論にすぎません。

あ~!戻り値ね!そうそう、それそれ。なんで言葉が出てこないかな~、とやや落胆しつつも、感覚が間違っていなかったことに嬉しくなりました。内部でどのような動きになっているのかを知るのが、今の楽しみの一つとなっています。


コードの「裏側」を想像してみる

コードが動いた喜びも大きいですが、こうして一つ一つの動きを解釈していくと、「なんとなく」で済ませていた部分がクリアになっていく面白さがありますね。

「セルに表示する」のではなく、「値を渡している」。 この違いに気づけたことで、コードの向こう側で動いているプログラムの様子が、少しだけイメージできるようになりました。用語を忘れても、この「仕組みの感覚」は大事にしていきたいです。

さて、今回は関数の「内部の動き」に注目しましたが、実は前回の記事で触れられなかった「ある図解」も、コンピュータの内部動作に関する重要な話でした。 私が「代入(置き換え)」だと思っている処理も、コンピュータから見ると少し違う景色が見えているようです。

次回は、その図解を振り返りながら、「プログラミングの理屈」について、じっくり考えてみたいと思います。


いいなと思ったら応援しよう!