2
3

More than 1 year has passed since last update.

高等学校 新教科 情報Ⅰ てほどき EXCEL VBAによるプログラミング準備編/初歩編

Last updated at Posted at 2022-01-08

高等学校 新教科 情報Ⅰ てほどき EXCEL VBAによるプログラミング準備編/初歩編

はじめに

2022年4月に普通科高校1年生となる世代から新教科 情報Ⅰが必修科目として取り入れられ、大学入試共通テストの科目として、この世代が受験する時より導入されます。
使用するプログラミング言語としては、Python , JavaScript , VBA , ドリトル , swiftを利用して進めるように解説資料が公開されています。
公開されている教員研修用教材 VBA版では、パソコンの準備方法の説明が乏しいので、本投稿では、迷わずに手っ取り早くVBAを利用できるよう、準備手順やプログラミングを簡単に解説します。
また、これからVBAを始めようとしている方でも役に立つ内容です。


VBAの特徴

  • Microsoft EXCELのVBAマクロとして使えるので、学校のコンピュータ室でEXCELを既に利用していたら、別途ソフトをインストールする必要なく設定を行うだけで使えます。
  • 統合環境でプログラミングを行うので、その場でステップ実行やブレークポイント設定が出来、プログラムの処理の流れ(ForループやIf分岐)を理解しやすい。
  • EXCELの表で入力された数値や文字列をデータとして取り扱いやすいのでプログラムを動かす準備に掛かる時間を節約できます。
  • 無料でダウンロードして利用できるLibreOfficeにはVBA互換動作機能が備わっているので、EXCELを利用できない場合は代替え利用ができます。
  • 大学生や社会人になってEXCELを使ってデータ処理を行う際に、VBAを使って手際よく処理するプログラミングを作れるようになっていると、役に立ちます。

準備(最初の1回目だけの設定)

  • 前提としてパソコンに既にEXCEL(2007以降)がインストールされていると想定します。(画面はバージョン2013で取得しています。)
  • 下図のように画面の最上部の「表示」と「アドイン」の間に「開発」と書かれたタブが表示されていたら設定済みなので、これ以上設定は必要ありません。
    EXCEL画面_設定済
     【設定手順】
  • EXCELの画面の左上の「ファイル」をクリックします。
  • 出てきたメニューの左下の「オプション」をクリックします。
  • オプションのダイアログの中の「リボンのユーザー設定」を選びます。
  • 「開発」の項目のチェックボックスにチェックを入れます。
  • この設定で、上の図のように「表示」と「アドイン」の間に「開発」が表示されます。
    EXCEL画面_設定手順

簡単なプログラムを実行する手順

実行させたら「Hello」と表示させるプログラム

Sub test()
    Debug.Print "Hello"     ' 表示を出力する
End Sub

上記の簡単なプログラムを実行させて、下記の実行結果を得るまでの手順を説明します。

Hello

・VBAの編集画面を開く

  • EXCELのファイルを新規作成する。
  • 「開発」⇒「Visual Basic」を選択する。
  • 「Sheet1」の部分をダブルクリックする。
  • プログラムを入力する部分が白くなったら入力できる状態です。
    EXCEL画面_編集

・プログラムを入力する

  • 白い入力する部分へプログラムを入力する。
  • その際、直接入力でもコピーペーストでも大丈夫です。
    EXCEL画面_入力

・実行させる

  • カーソルを「Debug.Print "Hello"」の行の先頭へ予め移動させておく

  • F5 キーを押すと実行される

  • 下の方の「イミディエイト」ウインドに結果が表示される
    EXCEL画面_実行

  • 下記の画面が出てきたら、Sheet1.testを選んで実行を押します。
    EXCEL画面_実行

・保存方法

VBA(VBAマクロ)を含まないEXCELのデータファイルは通常、拡張子 .xlsx のファイルとして保存されています。ところがこの形式では、VBAマクロは保存されません。
そこで、今回のようにVBAマクロを含んだ場合は、拡張子 .xlsmのファイルとして保存しなければなりません。方法は、下図のように名前を付けて保存する際にファイル形式を選ぶようにします。
EXCEL画面_保存


プログラム 基本的な命令

繰り返し処理 For Next ループ

先ほどと同じ要領で次のプログラムを入力して、F5キーを押して実行してみて下さい。

Sub test()
    Dim n As Long               ' 変数 n を使うための宣言
    For n = 1 To 5              ' n を 1 ~ 5 に変化させ繰り返す
        Debug.Print n           ' n の値を表示する
    Next n                      ' ここまでが繰り返しの対象
End Sub

同じ処理について回数を決めて実行を繰り返す為に、このFor Nextループを使います。
このプログラミングを実行したら、実行結果として1から5が表示されます。
変数 n が、順番に1,2,3,4,5と増えて行きその表示が出てきます。
繰り返して実行される部分は、For と Next に挟まれた行の命令です。

1
2
3
4
5

条件分岐 If Then

Sub test()
    Dim n As Long
    For n = 1 To 5
        If n >= 3 Then              ' 条件分岐 n が 3以上の時だけ実行
            Debug.Print "3以上  ";  ' 追加で表示する部分
        End If                      ' 条件分岐の終り
        Debug.Print n
    Next n
End Sub

ある条件の時だけ命令を実行したいという場合にIf Thenの条件分岐の命令を使います。
この場合は、変数 n の値が 3 以上の時だけ "3以上 "と表示を付け加えます。
行末に「;」セミコロンをつけると改行せずに続けて表示されます。

 1 
 2 
3以上   3 
3以上   4 
3以上   5 

デバック画面 ステップ実行

プログラムを実行すると一瞬で結果が表示されますが、これをゆっくりと実行してコマ送りで1行毎に動作を確認する方法があり、「ステップ実行」と言います。
これまでは、実行開始時にF5キーを押して実行しましたが、代わりに F8 キーを押してください。
下図のように黄色の部分が繰り返して F8 キーを押すと次に移動してゆきます。これにより1命令ごとにプログラムを実行する事が出来ます。
変数の上へマウスポインターを移動させると、今の変数の値が表示されるので確認できます。
最後まで一気に実行させたい時は、F5 キーを押して下さい。

EXCEL画面_step


プログラム 九九の表

Sub test()
    Dim r As Long, c As Long
    For r = 1 To 9
        For c = 1 To 9
            Debug.Print r * c;      ' 掛け算した結果を表示
        Next c
        Debug.Print
    Next r
End Sub

上記の九九の表を作るプログラムを実行したら、下記の結果が表示されます。

 1  2  3  4  5  6  7  8  9 
 2  4  6  8  10  12  14  16  18 
 3  6  9  12  15  18  21  24  27 
 4  8  12  16  20  24  28  32  36 
 5  10  15  20  25  30  35  40  45 
 6  12  18  24  30  36  42  48  54 
 7  14  21  28  35  42  49  56  63 
 8  16  24  32  40  48  56  64  72 
 9  18  27  36  45  54  63  72  81 

このプログラムはFor Next ループを2重にしています。
変数 C のForループは横方向に表示を9回繰り返します。
変数 r のForループは縦方向に9回繰り返します。その時に横方向のループを実行します。
Debug.Print r * c; の部分を Debug.Print r * c; r&; "," & c; に書き換えて実行すると下記のように変数 r と 変数 c の値が表示されるので流れを理解できます。

 1  1 ,1 2  1 ,2 3  1 ,3 4  1 ,4 5  1 ,5 6  1 ,6 7  1 ,7 8  1 ,8 9  1 ,9
 2  2 ,1 4  2 ,2 6  2 ,3 8  2 ,4 10  2 ,5 12  2 ,6 14  2 ,7 16  2 ,8 18  2 ,9
 3  3 ,1 6  3 ,2 9  3 ,3 12  3 ,4 15  3 ,5 18  3 ,6 21  3 ,7 24  3 ,8 27  3 ,9
 4  4 ,1 8  4 ,2 12  4 ,3 16  4 ,4 20  4 ,5 24  4 ,6 28  4 ,7 32  4 ,8 36  4 ,9
 5  5 ,1 10  5 ,2 15  5 ,3 20  5 ,4 25  5 ,5 30  5 ,6 35  5 ,7 40  5 ,8 45  5 ,9
 6  6 ,1 12  6 ,2 18  6 ,3 24  6 ,4 30  6 ,5 36  6 ,6 42  6 ,7 48  6 ,8 54  6 ,9
 7  7 ,1 14  7 ,2 21  7 ,3 28  7 ,4 35  7 ,5 42  7 ,6 49  7 ,7 56  7 ,8 63  7 ,9
 8  8 ,1 16  8 ,2 24  8 ,3 32  8 ,4 40  8 ,5 48  8 ,6 56  8 ,7 64  8 ,8 72  8 ,9
 9  9 ,1 18  9 ,2 27  9 ,3 36  9 ,4 45  9 ,5 54  9 ,6 63  9 ,7 72  9 ,8 81  9 ,9

九九の表をEXCELの表へ入れる

Sub test()
    Dim r As Long, c As Long
    For r = 1 To 9
        For c = 1 To 9
            Cells(r, c).Value = r * c   ' 値を表のセルに代入する処理
        Next c
    Next r
End Sub

つぎはこのプログラミングを実行しましょう。実行すると下図のようにEXCELの表に値が代入されて九九の表が出来上がります。
r * c で計算した値を表へ代入するために、Cells(r, c).Value を使います。
(r, c)はセルの中の位置を表しており、(2,3)であればセルC3の意味になります。

EXCEL画面_99


プログラム 素数(配列活用)

Sub test()
    Dim j As Long, k As Long, n As Long ' long型は32ビット変数
    Dim a() As Boolean
    Const nMax = 100                    ' 求める最大の素数は100
            
    ReDim a(nMax)                       ' マークを入れる配列を確保
    For j = 2 To nMax
        For k = j To nMax
            n = j * k
            If n > nMax Then
                Exit For                ' 掛けた数が最大の素数を超えるとループを脱出
            End If
            a(n) = True                 ' 素数でない数のインデックスをマークする
        Next k
    Next j
    
    For j = 2 To nMax
        If a(j) <> True Then            ' マークされていないインデックスの値を表示
            Debug.Print j;
        End If
    Next j
End Sub

上記の100までの素数を見つけ出すプログラム実行したら下記の結果になります。

 2  3  5  7  11  13  17  19  23  29  31  37  41  43  47  53  59  61  67  71  73  79  83  89  97 

「エラトステネスの篩」のアルゴリズムを実装したこのプログラムはFor Next ループを2重にしています。
配列 a() を使って、素数ではない数値のインデックスの配列の値をTrueにしてマークを入れておきます。マークが入れ終わったら、Trueになっていない配列のインデックスを表示するとそれが素数となります。

プログラム LED表示器

教員研修用教材のPython版(学習12外部装置との接続/5LEDを制御する)では、micro:bit製のLED表示基板が用いた例題が示されています。ところが VBA版では割愛され省略されていました。
そこで代わりにVBAで模擬的にソフトでLED表示器(LED_DisplaySimulator)を作成しましたのでご活用下さい。このLED_DisplaySimulatorは、別のVBAプログラムからのデータ受け取り以外に、テキストファイル経由でのデータ受け取りも行えるようにしましたので、Pythonでテキストファイルを出力する処理を作成したら、Pythonでの表示器として利用可能です。
なお、取得方法は「プログラムのダウンロード」の部分をご覧ください。
EXCEL画面_99

呼び出す側のプログラム例

Sub test()
    Dim r As Long
    With Workbooks("LED_DisplaySimulator.xlsm").Sheets("LedData")
        .Range("LEDデータ").Value = 0
        For r = 1 To 7
            .Range("LEDデータ").Cells(r, 3).Value = 9
        Next r
    End With
End Sub

このプログラムで、別のEXCELファイルのシートにデータを書き込むと表示が赤いドットが縦方向になります。
なお、ステップ実行させる時に F8 キーを今まで押していましたが、今回のプログラムで F8 キーでステップ実行したら、LED_DisplaySimulator.xlsm 側の処理に入っていき分かりにくくなります。その時は、 SHIFT+F8 キーでステップオーバーで実行するようにして下さい。
そうすると別の処理に入り込まないので分かりやすくなります。

f = open('X:\Temp\LED_Luminance.txt', 'w')
f.write('00900\n')
f.write('09900\n')
f.write('00900\n')
f.write('00900\n')
f.write('00900\n')
f.write('00900\n')
f.write('09990\n')
f.close()

Pythonでこのようにテキストファイルを作成すると、ファイルを読み込ませて表示する事ができます。

表示器側のプログラム

Const nLEDSize = 10                     ' LEDのサイズ
Const sLabelName = "LED_"               ' ラベル名称の先頭文字

Private objLabel() As MSForms.Label     ' LEDを模したラベルのオブジェクト配列


' LEDの明るさデータを受け取って画面のラベルを変更する
Public Sub setLedData(ByRef rData As Range)
    Dim r As Long, c As Long
    Dim nLuminance As Long
    Dim nRed As Long
    With rData
        For r = 1 To nRowCount                              ' ループ 縦
            For c = 1 To nColCount                          ' ループ 横
                nLuminance = .Cells(r, c).Value             ' セルから値を取り込む
                If nLuminance < 0 Then nLuminance = 0       ' 0~9の範囲に入れ込む
                If nLuminance > 9 Then nLuminance = 9
                nRed = nLuminance * 28                      ' 赤色の明るさに変更
                objLabel(r, c).BackColor = RGB(nRed, 0, 0)  ' ラベルに色を付ける
            Next c
        Next r
    End With
    DoEvents                                                ' 画面描画
End Sub


' ラベルなどのコントロールの一覧を表示する(確認用)
Private Sub dumpElements()
    Dim ctrl As Control
    For Each ctrl In Me.Controls
        Debug.Print ctrl.Name                               ' ラベルなどのコントロールの名称を表示
    Next ctrl
End Sub


' 初期化時の処理
' LED用ラベルをフォーム上に動的に作成し表示する
Private Sub UserForm_Initialize()
    Dim nWidthPitch As Double, nHeightPitch As Double
    Dim r As Long, c As Long
    ReDim objLabel(nRowCount, nColCount)                    ' ラベルの配列を初期化
    With Me
        nWidthPitch = .Width / (nColCount + 1)              ' ピッチを設定 横
        nHeightPitch = .Height / (nRowCount + 2)            '               縦
        .BackColor = RGB(0, 127, 0)                         ' プリント基板相当の背景色
        For r = 1 To nRowCount                              ' ループ 縦
            For c = 1 To nColCount                          ' ループ 横
                Set objLabel(r, c) = .Controls.Add("Forms.Label.1", sLabelName & r & "_" & c)
                                                            ' ここでラベルを動的生成
                With objLabel(r, c)                         ' ラベルの設定
                    .Left = nWidthPitch * c - nLEDSize / 2  ' 位置 横
                    .Top = nHeightPitch * r - nLEDSize / 2  ' 位置 縦
                    .Height = nLEDSize                      ' 高さ
                    .Width = nLEDSize                       ' 幅
                    .Caption = ""                           ' 文字は無し
                    .BackColor = RGB(0, 0, 0)               ' 黒色
                End With
            Next c
        Next r
    End With
    g_bIsInitialized = True                                 ' 初期化完了フラグ
    'Call dumpElements
End Sub


' 終了時の処理
Private Sub UserForm_Terminate()
    Dim r As Long, c As Long
    For r = 1 To nRowCount
        For c = 1 To nColCount
            Set objLabel(r, c) = Nothing                    ' オブジェクトを解放
        Next c
    Next r
    g_bIsInitialized = False                                ' 初期化完了フラグをリセット
    Call unregisterTimer                                    ' タイマーをリセット
End Sub

表示器の部分(緑色のバックで赤色のLEDに模したラベルを表示)は、このようなプログラムとなっており、フォーム上に35個のラベルを動的に作成して配置しています。

Public g_bIsInitialized As Boolean                      ' 初期化完了フラグ

Public Const nRowCount = 7                              ' LED数 縦7個
Public Const nColCount = 5                              ' LED数 横5個
Public Const cLedData = "LedData"                       ' データを格納しているシート名
Public Const cFileName = "LED_Luminance.txt"            ' ファイル経由でデータを受け取る時のファイル名

Public nextTimer As Date


' タイマーを解除する(終了時に呼ばれる処理)
Public Sub unregisterTimer()
    If nextTimer > 0 Then
        Application.OnTime nextTimer, "entryByTimer", , False   ' 予約済みタイマーを解除する
        nextTimer = 0
        With ThisWorkbook.Sheets(cLedData)
            .OLEObjects("LabelSeconds").Object.Caption = ""     ' 秒表示ラベルをクリア
            .OLEObjects("CheckBoxRepeat").Object.Value = False  ' 繰り返しのチェックを外す
        End With
    End If
End Sub

' 1秒ごとに呼び出されるタイマー処理
Public Sub entryByTimer()
    nextTimer = 0
    Call readDataFromFile                               ' ファイルからデータを読み出し
        
    If Sheets(cLedData).OLEObjects("CheckBoxRepeat").Object.Value = False Then
        Exit Sub                                        ' 繰り返しのチェックが外れていたら終了する
    End If
    
    nextTimer = Now() + TimeValue("00:00:01")           ' 次に呼び出す1秒先の時刻を計算
    ThisWorkbook.Sheets(cLedData).OLEObjects("LabelSeconds").Object.Caption = Second(nextTimer)
                                                                ' 動作モニター用に秒を表示
    Application.OnTime nextTimer, "entryByTimer"        ' 次の呼び出しをタイマー予約
End Sub



' ファイルからのデータ読み出し
Public Sub readDataFromFile()
    Dim nFn As Long
    Dim strFullPath As String, strLedData As String
    Dim r As Long, c As Long
    Dim v(1 To nRowCount, 1 To nColCount) As Variant    ' 読みだしたデータ格納用の配列を宣言

    nFn = FreeFile                                      ' ファイルハンドルを取得
    strFullPath = ThisWorkbook.Path & "\" & cFileName   ' フォルダー名+ファイル名のフルパスを設定
    If Dir(strFullPath, vbNormal) = "" Then Exit Sub    ' ファイルが存在していないときは終了
    Open strFullPath For Input As #nFn                  ' データファイルを開く
    For r = 1 To nRowCount                              ' ループ 縦
        Input #nFn, strLedData                          ' 1行分読みだす
        For c = 1 To nColCount                          ' ループ 横
            v(r, c) = Val(Mid(strLedData, c, 1))        ' 1文字分のデータを配列へ登録
        Next c
    Next r
    Close #nFn                                          ' 処理が終わったのでファイルを閉じる
    ThisWorkbook.Sheets(cLedData).Range("LEDデータ").Value = v
                                                        ' 配列のデータ(7*5個)をEXCEL表へ転記する
End Sub

このプログラムを使って、テキストファイルを読み込んでEXCELへデータを取り込む処理と、1秒おきにファイル読み込みを繰り返す処理を行っています。

動かし方

LED_DisplaySimulator.xlsm をEXCELで開いて、左上のオレンジ色の部分35個の数字を0~9の範囲で変更してみて下さい。LED表示器の表示が変わるはずです。
次にLED_SimSample.xlsm も開いて下さい。(合計2個のEXCELを開いていることになります。)
この中のVBAプログラムを実行するとLED表示器の表示が変更されるはずです。
このプログラムでは、コマンドボタンやユーザーフォームを使用しています。これらは参考ホームページの中のリンクをご覧ください。

プログラム 別のAPI活用 東京電力の電力供給量

教員研修用教材 VBA版 の中の学習14応用プログラム・演習4では、APIを使ってインターネット上からデータを取得する方法の説明がありました。
別のAPIを使う方法として東京電力の電力供給量APIから取得したデータをEXCELの表へ格納するVBAプログラミングを作成しました。
表へ入れる項目は、 日付・時刻、電力供給量、供給可能最大量 の3項目です。
なお、取得する方法は研修用教材で説明されていたい方法と同じです。
(2022/1/10追加)

Private powerList As Variant                        ' 取得した結果を入れる配列
Private powerListCount As Long                      ' 配列のデータレコード数


' 電力供給量のボタンを押した時に開始される処理
'
Private Sub CommandButton1_Click()
    Dim strPowerUsage As String                     ' 取得結果(生データ)を入れる文字列
    strPowerUsage = getPowerUsage("2022/1")         ' データ取得する年/月
    Call convertJson2List(strPowerUsage)            ' データの形式を分かりやすく配列に変換する
    
    Dim r As Range
    With Sheets("演習4-他のAPI東京電力").Range("供給状況APIで取得したデータ")
        If .Offset(1, 0).Value <> "" Then
            Set r = .CurrentRegion
            If Not (r Is Nothing) Then
                r.ClearContents                     ' 以前に取得したデータをEXCEL表からクリアする
                r.Cells(1, 1).Value = "供給状況APIで取得したデータ"
            End If
        End If
        Set r = Range(.Cells(2, 1), .Cells(powerListCount + 1, 3))
        r.Value = powerList                         ' 配列に格納しているデータをEXCEL表へ転記する
    End With
End Sub

' 電力使用量を取得するAPI呼び出し
Function getPowerUsage(strYearMonth As String) As String
    Dim objXMLHttp As Object, zipArr
    
    Set objXMLHttp = CreateObject("MSXML2.XMLHTTP")
    objXMLHttp.Open "GET", "https://tepco-usage-api.appspot.com/" & strYearMonth & ".json", False
                                                    ' 電力使用量を取得するAPIを呼び出す
    objXMLHttp.Send
    getPowerUsage = objXMLHttp.responseText         ' 電力使用量を戻り値文字列に入れる
    Set objXMLHttp = Nothing
End Function


' 取得したJson形式のデータを変換して見やすくする
Private Sub convertJson2List(denryoku As String)
    Dim nRow As Long, vPowerHour As Variant
    vPowerHour = Split(denryoku, "},")              ' 1レコード単位の配列に一旦格納する
    powerListCount = UBound(vPowerHour)             ' レコード数を求める

    ReDim powerList(powerListCount, 1 To 3)         ' 結果を入れる入れる配列を宣言して領域を確保する
    Call getEachItem(1, "entryfor", vPowerHour)     ' データの日付・時刻を配列に入れる
    Call getEachItem(2, "usage", vPowerHour)        ' データの電力供給量を配列に入れる
    Call getEachItem(3, "capacity", vPowerHour)     ' データの電力供給可能最大量を配列に入れる
    
    Call dumpPowerList
End Sub


' 1項目ごとのデータを配列に入れる
Private Sub getEachItem(nIdx As Long, strTitle As String, vPowerHour As Variant)
    powerList(0, nIdx) = strTitle                   ' 配列の最初の部分は表題欄として使う
    Dim n As Long, nPos As Long, nPos2 As Long, nPos3 As Long, strRecord As String
    For n = 1 To powerListCount
        strRecord = vPowerHour(n - 1)               ' レコード中の項目名の部分の場所を特定する
        nPos = InStr(1, strRecord, Chr(34) & strTitle & Chr(34))    ' 項目名の始まりの位置
        nPos2 = InStr(nPos, strRecord, ":") + 2                     ' 項目名の終りの位置
        nPos3 = InStr(nPos2, strRecord, ",")                        ' 値の終りの位置
        If nPos3 = 0 Then
            nPos3 = Len(strRecord) - 2              ' 最後の項目はカンマで終わらない対策
        End If
        powerList(n, nIdx) = Replace(Mid(strRecord, nPos2, nPos3 - nPos2), Chr(34), "")
                                                    ' 項目名に応じた値を切り出して、配列に入れる
    Next n
End Sub


' 配列の中身を表示する(意図したとおりに取得できているかの確認用)
Private Sub dumpPowerList()
    Dim n As Long
    For n = 0 To powerListCount
        Debug.Print powerList(n, 1), powerList(n, 2), powerList(n, 3)
    Next n
End Sub

なお、このEXCELのダウンロード方法は「プログラムのダウンロード」の部分をご覧ください。

プログラム n! 再起呼び出しによる階乗の計算プログラム (2022/1/16追記)

階乗 ( n * ・・・ * 3 * 2 * 1 n~1までの整数を掛け算)を計算するプログラミングの例です。
再起呼び出しと言って、関数の中で自分自身を呼び出して答えを返す方式で実装しています。

' 階乗を求めるプログラム  ここから実行を始める
Private Sub testFactorial()
    Dim ans As Long
    ans = factorial(5)
    Debug.Print "階乗="; ans
End Sub

' 階乗を求めるプログラム  実際の処理
Private Function factorial(n As Long) As Long
    Dim f As Long
    If n > 1 Then
        f = n * factorial(n - 1)        ' 1個少ない計算値と掛け算を再起呼び出しで繰り返す
    Else
        f = 1                           ' 1まで達したら終わる
    End If
    Debug.Print n                       ' 途中経過の表示
    DoEvents                            ' もし無限ループになった時、ブレークするおまじない
    factorial = f                       ' 戻り値(関数の答え)を設定する
End Function

このプログラムを実行すると次のような答えとなります。
ポイントは、f = n * factorial(n - 1) の部分で、自分自身の関数呼び出しを繰り返している点です。
呼び出された、関数の値などは、スタックと呼ばれるメモリー領域に別々に記憶されるので、前回の呼び出し分と混ざらないでデータを取得できます。
是非、F8キーを押してステップ実行を行ってプログラムの流れを目で追って見てください。

 1 
 2 
 3 
 4 
 5 
階乗= 120 

プログラム 矢印キーを使って、EXCELの表の●を動かすプログラム (2022/1/16追記)

EXCEL画面_矢印キー

このようなEXCELの表の中にある●を矢印キーを使って動かすプログラミングの例です。

Private Declare PtrSafe Function Sleep Lib "kernel32.dll" (ByVal t As Long) As Long
Private Declare PtrSafe Function GetKeyState Lib "User32" (ByVal vKey As Long) As Integer

Const VK_SHIFT = 16                 ' シフト
Const VK_LEFT = 37                  ' 左
Const VK_UP = 38                    ' 上
Const VK_RIGHT = 39                 ' 右
Const VK_DOWN = 40                  ' 下


Const fieldSize = 21                ' 移動できるエリアは 行21と列Uまでに制限

Private mXpos As Long               ' 表示する ● の位置 X,Y
Private mYpos As Long


' ここから開始する
Private Sub moveByArrowkey()
    Dim n As Long, nKey As Long
    Call initField                              ' 画面を初期化する処理を呼び出し
    For n = 1 To 10000                          ' 10000*300ms で 約50分までの動作に制限
        ThisWorkbook.Activate
        Cells(mYpos, mXpos).Select              ' ●の位置にセルカーソルを合わせる
        Cells(mYpos, mXpos).Value = ""
        nKey = getKeyKind()                     ' どのキーが押されたかを調べる
        
        Select Case nKey:
            Case VK_LEFT                        ' ← 左 の処理
                mXpos = mXpos - 1
            Case VK_RIGHT                       ' → 右 の処理
                mXpos = mXpos + 1
            Case VK_UP                          ' ↑ 上 の処理
                mYpos = mYpos - 1
            Case VK_DOWN                        ' ↓ 下 の処理
                mYpos = mYpos + 1
        End Select
        
        If mXpos < 1 Or mXpos > fieldSize Or mYpos < 1 Or mYpos > fieldSize Then
            Call initField                      ' 移動可能エリアの端に来たら初期化して真ん中に戻す
        End If
        
        Cells(mYpos, mXpos).Value = "●"        ' ●を書き込む
        DoEvents
        Sleep (300)                             ' 300ms時間を待つ(win32 APIを使用)
    Next n
End Sub


 ' どのキーが押されたかを調べる処理
Private Function getKeyKind() As Long
    Dim nKey As Long
    nKey = 0
    If GetKeyState(VK_SHIFT) < 0 Then
        nKey = VK_SHIFT                         ' シフトキーを押したら終了する
        MsgBox ("終了しました")
        End
    ElseIf GetKeyState(VK_LEFT) < 0 Then        ' どのキーがをWin32 APIを使用して調べる。
        nKey = VK_LEFT                          ' 左
    ElseIf GetKeyState(VK_UP) < 0 Then
        nKey = VK_UP                            ' 上
    ElseIf GetKeyState(VK_RIGHT) < 0 Then
        nKey = VK_RIGHT                         ' 右
    ElseIf GetKeyState(VK_DOWN) < 0 Then
        nKey = VK_DOWN                          ' 下
    End If
    getKeyKind = nKey
End Function


' 初期化処理
Private Sub initField()
    With Range(Cells(1, 1), Cells(fieldSize, fieldSize + 1))
        .ClearContents                          ' 書かれている文字を消去する
        .EntireColumn.ColumnWidth = 2           ' 列幅を狭くする
    End With
    With Range(Cells(1, fieldSize + 1), Cells(fieldSize, fieldSize + 1))
        .Interior.Color = RGB(0, 0, 0)          ' 右端の境界線を黒くする
    End With
    With Range(Cells(fieldSize + 1, 1), Cells(fieldSize + 1, fieldSize + 1))
        .Interior.Color = RGB(0, 0, 0)          ' 下端の境界線を黒くする
    End With
    mXpos = fieldSize / 2                       ' 位置座標を真ん中にする
    mYpos = fieldSize / 2
End Sub

使い方は、moveByArrowkey() の部分からF5キーを押して実行します。
実行後は、VBA画面ではなく、EXCELの表の方をクリックしてフォーカスを当てて下さい。矢印キー(上下左右キー)を押すと●が移動します。
終了する際は、シフトキーを押して下さい。

紹介したプログラムのダウンロード

下記のURLからダウンロードして頂くことが出来ます。
取得したZIPファイルを展開して頂くとフォルダーに分けてプログラムを入れています。
http://va030157.starfree.jp/vb001/VBA_Sample.zip

(httpsで無いので、ブラウザよっては弾かれてダウンロード出来ない場合があります。
その場合は、リンクをコピーして別のウインドで開いて下さい。)

こちらにも同じファイルを保管しました。(2022/1/15追記)
https://drive.google.com/file/d/1179fC9Syn_jAVxNsqtSgMzwjouZug5-E/view?usp=sharing


参考ホームページ

文部科学省

情報Ⅰ (教員向け)
https://www.mext.go.jp/a_menu/shotou/zyouhou/detail/1416756.htm
https://www.mext.go.jp/a_menu/shotou/zyouhou/detail/1421808.htm
教員研修用教材 VBA版
https://www.mext.go.jp/content/20200716-mxt_jogai01-000001169_002.pdf
検定済み教科書一覧
https://www.mext.go.jp/content/20210604-mxt_kyokasyo02-000014470_4.pdf

教科書出版社のホームページ

第一学習社 https://www.daiichi-g.co.jp/pr/tb/9/81
数研究出版 https://www.chart.co.jp/kyokasho/22kou/joho/kou/
日本文教出版 https://www.nichibun-g.co.jp/textbooks/joho/2022_joho01_1/textbook/
東京書籍 https://www.tokyo-shoseki.co.jp/textbook/h/7/
実教出版 https://www.jikkyo.co.jp/highschool_r04/jouhou/textbook/r04/
開隆堂 https://www.kairyudo.co.jp/contents/03_ko/joho/r4/index.htm

EXCEL VBA関係ホームページ

VBAマクロの初期設定
http://excel-vba.jpn.org/section/3/Init-Excel

VBAの文法解説
https://qiita.com/am10/items/ad786e98446264201f7e

コマンドボタンの追加方法
https://support.microsoft.com/ja-jp/office/%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A0%E3%81%BE%E3%81%9F%E3%81%AF%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%AB-%E3%83%9C%E3%82%BF%E3%83%B3%E3%81%AB%E3%83%9E%E3%82%AF%E3%83%AD%E3%82%92%E5%89%B2%E3%82%8A%E5%BD%93%E3%81%A6%E3%82%8B-d58edd7d-cb04-4964-bead-9c72c843a283

ユーザーフォーム(UserForm)の解説 (動画)
https://www.youtube.com/watch?v=62vdzRK6eKo
https://www.youtube.com/watch?v=i8nQT1GSmg0

API関係:東京電力の電力供給量
https://tepco-usage-api.appspot.com/
https://tepco-usage-api.appspot.com/2022/1.json

Python関係ホームページ

「情報Ⅰ」教員研修用教材の解説(Python中心) 諏訪東京理科大学
https://www.sus.ac.jp/localarea/for_teachers/intelligence_department/

Pythonプログラミング入門(力作の動画あり)
https://qiita.com/toppakou7/items/9e115b0ad3e08d01b1bc

アルゴリズムの解説

エラトステネスの篩
https://ja.wikipedia.org/wiki/%E3%82%A8%E3%83%A9%E3%83%88%E3%82%B9%E3%83%86%E3%83%8D%E3%82%B9%E3%81%AE%E7%AF%A9

LibreOfficeのインストール

https://www.ja-fukuoka.or.jp/upload/user/ExceltoCalc.pdf
https://nashiza-blog.com/2020/05/17/libreoffice-vba/

著作権について

短いサンプルプログラムが中心なので、主張出来るような著作権は無いと考えています。
利用については、クリエーティブコモンライセンス的な扱いを想定しています。
個人での利用、学校や企業での利用など、無償で自由に使って頂いて問題ありません。
なお、役に立った場合、制作者としては、コメントで感想などを寄せて頂けると励みになります。

記号の読み方

セミコロン
コロン
クォーテーション / ダブルクォーテーション
アポストロフィー / シングルクォーテーション
アスタリスク
パウンド(英語) / シャープ(日本語)
シャープ

2
3
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3