Excel数式で学ぶ再帰処理

LWP | Excel数式で学ぶ再帰処理

LWP TECHNICAL ARTICLE | 159

Excel数式で学ぶ再帰処理

LAMBDAで理解する1重ループと2重ループ

Copyright © 2026 LWP 山中 一弘

本資料は、出典を明記いただければ、商用・非商用を問わず、ご自由に複製・改変・再配布していただけます。なお、著作権表示は改変せず、そのまま記載してご利用くださいますようお願いいたします。

記事要約

Excelの数式は、セルの値を計算するだけのものではありません。LETとLAMBDAを組み合わせ、現在位置と途中結果を引数として受け渡すと、VBAの1重ループや2重ループに相当する処理を、1つの数式として組み立てられます。

本記事では、階乗による最小の再帰から始め、名前定義を使う方法と使わない方法、LETを先頭に置く書き方とLAMBDAを先頭に置く書き方、配列を走査する1重ループ、行と列を分けて考える2重ループまでを段階的に説明します。完成式を暗記するのではなく、「終了条件」「状態の変化」「再呼び出し」「途中結果の蓄積」を自分で設計できることが到達点です。

本記事の対象とゴール

想定読者

  • LETとLAMBDAの基本は分かるが、再帰数式の読み方が分からない人

  • VBAのループ処理を、Excel数式の状態遷移として理解したい人

  • 1セルで完結する高度な動的配列数式を、構造から設計したい人

本記事で得られること

  1. 再帰を成立させる終了条件・状態変化・再呼び出しを見分けられます。

  2. F(F,5)やSelf(Self,n-1)で関数自身を渡す理由を説明できます。

  3. 1重ループと2重ループを、位置と途中結果を持つ状態遷移として設計できます。

  4. MAP、MAKEARRAY、REDUCE、SCANと再帰LAMBDAの使い分けを判断できます。

目次

  • はじめに Excel数式でも再帰処理を書ける

  • 第1章 再帰処理の基本

  • 1.1 再帰とは何か

  • 1.2 再帰を成立させる三つの要素

  • 1.3 数式における再帰

  • 1.4 再帰と循環参照の違い

  • 1.5 階乗による最小例

  • 第2章 Excel数式で再帰関数を作る方法

  • 2.1 LAMBDAとLETの役割

  • 2.2 名前定義を使用する再帰

  • 2.3 名前定義を使用しない再帰

  • 2.4 Fを2回指定する理由

  • 2.5 LETを先頭に置く書き方

  • 2.6 LAMBDAを先頭に置く書き方

  • 2.7 二つの書き方の使い分け

  • 第3章 再帰による1重ループ

  • 第4章 再帰による2重ループ

  • 第5章 再帰を使う場面と使わない場面

  • まとめ 数式で再帰を書くための設計手順

はじめに Excel数式でも再帰処理を書ける

0.1 本資料の目的

Excelで処理を繰り返すと聞くと、VBAのFor文やDo文を思い浮かべる人が多いでしょう。しかし、現在のExcelでは、LAMBDAによって数式の中に関数を定義できます。その関数から同じ関数を再び呼び出せば、数式だけで再帰処理を構築できます。

再帰とは、単に同じ処理を何度も実行することではありません。現在の状態を引数として受け取り、次の状態を作り、終了条件に達するまで同じ処理へ渡し続ける考え方です。

本資料では、次の3段階を扱います。

単純な再帰

5! = 5 × 4 × 3 × 2 × 1 = 120

数値を1ずつ減らしながら、自分自身を呼び出します。

1重ループ

I1:I3のa、b、cを順番に処理し、aa、bb、ccを返します。

2重ループ

I1:J3の各セルを行方向・列方向に処理し、元と同じ形の2次元配列を返します。

元データ 処理結果
a 1 aa 11
b 2 bb 22
c 3 cc 33

この変換だけならMAPで簡単に書けます。ここで再帰を使うのは、値を2回連結するためではなく、数式内で現在位置と途中結果を管理し、1重・2重の走査処理を組み立てる方法を理解するためです。

0.2 本資料で使用する主な関数

関数 役割
LAMBDA 引数を取る処理を定義する
LET 値やLAMBDAに数式内だけの名前を付ける
IF 終了と継続を分ける
INDEX 行番号・列番号から値を取得する
ROWS・COLUMNS 配列の行数・列数を取得する
HSTACK・VSTACK 結果を横・縦方向へ連結する

第1章 再帰処理の基本

1.1 再帰とは何か

再帰とは、ある処理が、その処理の中から同じ処理を再び呼び出す構造です。

たとえば、5の階乗は次のように分解できます。

5の階乗 = 5 × 4の階乗
4の階乗 = 4 × 3の階乗
3の階乗 = 3 × 2の階乗

処理対象の数値は変わりますが、「現在の数値を掛け、1小さい数値で同じ処理を行う」という形は変わりません。大きな問題を少し小さな同型の問題へ分解し、同じ処理へ渡しているわけです。

1.2 再帰を成立させる三つの要素

再帰には、必ず次の三つが必要です。

終了条件

どの状態になったら、自分自身を呼び出すのをやめるかを決めます。階乗では、n<=1になったら1を返します。

IF(n<=1,1,継続処理)

状態の変化

次の呼び出しでは、現在とは異なる状態を渡します。階乗ではn-1です。

5 → 4 → 3 → 2 → 1

状態が終了条件へ近づかなければ、処理は停止しません。

自分自身の再呼び出し

状態を変えたうえで、同じ処理をもう一度実行します。名前の付いた関数なら、概念的には次の形です。

FACT(n-1)

名前定義を使わない方法では、後で説明するSelfを使います。

Self(Self,n-1)

再帰数式を読むときは、まず「終了条件」「変化する状態」「再呼び出し」の三つを探します。

1.3 数式における再帰

LAMBDAを使うと、Excel数式の中に引数を取る処理を定義できます。

=LAMBDA(x,x*2)(5)

この式は、xを受け取ってx*2を返すLAMBDAを定義し、末尾の(5)で直ちに実行しています。結果は10です。

基本構造は次のとおりです。

LAMBDA(引数,処理)(実際に渡す値)

数式での再帰は、このLAMBDAの処理の中から同じLAMBDAを再び呼び出すことです。

1.4 再帰と循環参照の違い

再帰とセルの循環参照は異なります。

A1セルに=A1+1と入力すると、A1がA1自身を参照する循環参照になります。一方、本資料の再帰は、セルを自分自身へ戻すのではありません。セル内のLAMBDAが、引数を変えて同じLAMBDAを呼び出します。

循環参照
セルA → セルA
 
再帰LAMBDA
F(5) → F(4) → F(3) → F(2) → F(1)

再帰では、各呼び出しの状態が変化し、終了条件に達すると値が確定します。

1.5 階乗による最小の再帰例

5の階乗は次のように展開されます。

F(5)
= 5 × F(4)
= 5 × 4 × F(3)
= 5 × 4 × 3 × F(2)
= 5 × 4 × 3 × 2 × F(1)
= 120

呼び出しは5から1へ進み、F(1)=1が確定すると、結果が逆方向へ戻ります。

F(1)=1
F(2)=2×1=2
F(3)=3×2=6
F(4)=4×6=24
F(5)=5×24=120

数式の中心部分は次の形です。

IF(
    n<=1,
    1,
    n*再帰呼び出し
)

第2章 Excel数式で再帰関数を作る方法

2.1 LAMBDAとLETの役割

LAMBDAは、引数と処理内容を定義します。

LAMBDA(x,y,x+y)

その場で実行する場合は、末尾に値を渡します。

=LAMBDA(x,y,x+y)(3,4)

LETは、数式の途中に名前を付けます。

=LET(
    x,10,
    y,20,
    x+y
)

LETでは、数値だけでなくLAMBDAにも名前を付けられます。

=LET(
    Twice,LAMBDA(x,x*2),
    Twice(5)
)

この性質を使って、数式内に再帰関数を定義します。

2.2 名前定義を使用する再帰

再帰を最も自然に書けるのは、名前の管理で関数名を定義する方法です。

名前をFACTとし、参照範囲へ次の式を登録します。

=LAMBDA(n,
    IF(
        n<=1,
        1,
        n*FACT(n-1)
    )
)

セルからは、通常の関数と同じように呼び出します。

=FACT(5)

自分自身の名前FACTを直接呼び出せるため、再帰の意味が読み取りやすい書き方です。また、ブック内の複数セルから再利用できます。

一方、関数本体は名前の管理にあり、セルの数式だけを見ても処理内容が分かりません。数式だけを別ブックへ移す場合も、名前定義を別途扱う必要があります。

2.3 名前定義を使用しない再帰

1セルだけで完結させる場合は、関数自身を引数として渡します。

=LET(
    F,LAMBDA(Self,n,
        IF(
            n<=1,
            1,
            n*Self(Self,n-1)
        )
    ),
    F(F,5)
)

Selfは予約語ではありません。関数自身を受け取る引数として付けた名前です。

Fは二つの引数を取ります。

Self:再帰関数自身
n:現在処理している数値

名前定義を使わない方法は数式が長くなりますが、1セルで完結し、数式をそのままコピーできます。本資料では、数式内部の仕組みを明示するため、この方法を中心に扱います。

2.4 Fを2回指定する理由

最も分かりにくいのが、次の呼び出しです。

F(F,5)

FはLAMBDA(Self,n,処理)として定義されています。したがって、引数の対応は次のとおりです。

仮引数 渡される値
Self F
n 5
F      ( F,    5 )
↑        ↑     ↑
実行     Self  n

1番目のFは実行する関数です。括弧内の2番目のFは、Selfへ渡す関数自身です。

内部の再帰呼び出しは次の形です。

Self(Self,n-1)

最初の呼び出しではSelf=F、n=5なので、実際にはF(F,4)になります。

F(F,5)
→ F(F,4)
→ F(F,3)
→ F(F,2)
→ F(F,1)

関数自身を毎回次の呼び出しへ渡すことで、名前定義がなくても再帰を継続できます。

2.5 LETを先頭に置く書き方

名前定義を使わない再帰の基本形です。

=LET(
    F,LAMBDA(Self,n,
        IF(
            n<=1,
            1,
            n*Self(Self,n-1)
        )
    ),
    F(F,5)
)

この式は、次の順番で読みます。

1. LETを開始する
2. 再帰関数Fを定義する
3. F(F,5)で処理を開始する

LETの最後の引数が数式全体の結果です。複数の補助関数や入力範囲を定義する1重・2重ループでは、この書き方が特に読みやすくなります。

2.6 LAMBDAを先頭に置く書き方

数式全体を、引数を受け取るLAMBDAとして包むこともできます。

=LAMBDA(a_x,
    LET(
        F,LAMBDA(Self,n,
            IF(
                n<=1,
                1,
                n*Self(Self,n-1)
            )
        ),
        F(F,a_x)
    )
)(5)

外側の構造は次のとおりです。

LAMBDA(a_x,内部処理)(5)

末尾の(5)が外側の引数a_xへ渡され、内部でF(F,a_x)が実行されます。セルの値を渡すこともできます。

=LAMBDA(a_x,
    LET(
        F,LAMBDA(Self,n,
            IF(n<=1,1,n*Self(Self,n-1))
        ),
        F(F,a_x)
    )
)(G1)

外側のLAMBDAは再帰していません。数式全体の入力を受け取り、内部の再帰関数へ渡す入口です。

2.7 二つの書き方の使い分け

観点 LET先頭 LAMBDA先頭
主な目的 内部処理を組み立てる 数式全体の引数を明示する
入力値 最後の呼び出しへ書く 外側のLAMBDAへ渡す
複数の補助関数 特に書きやすい 内部のLETで定義する
代表用途 1重・2重ループ 入力を引数化したい式

内部の再帰原理は同じです。本資料の1重・2重ループでは、構造を見やすくするためLETを先頭に置きます。

第3章 再帰による1重ループ

3.1 再帰をループとして使う考え方

階乗ではnを1ずつ減らしました。配列を上から下へ処理する場合は、行番号rを1ずつ増やします。

処理 現在位置 次の状態 終了条件
階乗 n n-1 n<=1
行方向 r r+1 r>ROWS(a)

配列処理では、行番号だけでなく、それまでに作成した結果も次の呼び出しへ渡します。

3.2 ループで保持する状態

1重ループでは、次の三つを引数にします。

LAMBDA(Self,r,z,処理)
引数 役割
Self 再帰関数自身
r 現在の行番号
z それまでの処理結果

一般的なループのカウンターに相当するのがr、結果を蓄積する変数に相当するのがzです。

ただし、Excel数式では同じ変数を書き換えません。新しい行番号と新しい結果を、次の呼び出しへ渡します。

Self(Self,r+1,VSTACK(z,新しい値))

3.3 1重ループの数式

I1:I3にa、b、cがあるとします。各値を2回連結する数式は次のとおりです。

=LET(
    a,I1:I3,
 
    WalkRows,
    LAMBDA(Self,r,z,
        IF(
            r>ROWS(a),
            z,
            LET(
                x,INDEX(a,r),
                Self(
                    Self,
                    r+1,
                    VSTACK(z,x&x)
                )
            )
        )
    ),
 
    WalkRows(
        WalkRows,
        2,
        INDEX(a,1)&INDEX(a,1)
    )
)

最初に入力範囲へaという名前を付け、再帰関数WalkRowsを定義しています。最後のWalkRows(...)が最初の呼び出しです。

3.4 結果を蓄積する変数z

再帰呼び出しの中心は次の部分です。

Self(
    Self,
    r+1,
    VSTACK(z,x&x)
)
  • Self:関数自身を次へ渡す

  • r+1:次の行へ進む

  • VSTACK(z,x&x):新しい値を結果の下へ追加する

1行目は初期値として先に処理し、2行目から再帰を始めています。

WalkRows(WalkRows,2,INDEX(a,1)&INDEX(a,1))

初期状態は次のとおりです。

r = 2
z = aa

この方法により、先頭へダミーの空白値を置かずに、正しい形の配列へ値を追加できます。

3.5 1重ループの実行追跡

呼び出し r z 動作
初期 2 aa 2行目のbを処理
2回目 3 {aa;bb} 3行目のcを処理
3回目 4 {aa;bb;cc} 行数を超えたため終了

r=4になると、次の条件が成立します。

r>ROWS(a)

元配列は3行なので、完成したzを返します。

aa
bb
cc

第4章 再帰による2重ループ

4.1 2重ループの全体構造

2次元の範囲を処理するには、行方向と列方向の両方を移動します。

1行目:1列目 → 2列目
2行目:1列目 → 2列目
3行目:1列目 → 2列目

本資料では、処理を次の三つへ分けます。

WalkRows
    └─ MakeRow
           └─ WalkCols
関数 役割
WalkCols 指定行を左から右へ処理する
MakeRow WalkColsを使って1行を完成させる
WalkRows 完成した行を上から下へ積み重ねる

すべてを一つの関数へ詰め込まず、列方向・1行の生成・行方向に役割を分けることが重要です。

4.2 列方向を処理するWalkCols

WalkCols,
LAMBDA(Self,r,c,z,
    IF(
        c>COLUMNS(a),
        z,
        LET(
            x,INDEX(a,r,c),
            Self(
                Self,
                r,
                c+1,
                HSTACK(z,x&x)
            )
        )
    )
)

引数の意味は次のとおりです。

引数 役割
Self WalkCols自身
r 処理対象の行
c 現在の列
z 作成途中の1行

列方向の処理中、rは固定され、cだけが増加します。

r = 1のまま
c = 2 → 3 → 4 ...

現在のセルはINDEX(a,r,c)で取得します。加工結果はHSTACKで右側へ追加します。

HSTACK(z,x&x)

cが列数を超えたら、その行は完成しているためzを返します。

4.3 1行を作成するMakeRow

MakeRow,
LAMBDA(r,
    LET(
        x,INDEX(a,r,1),
        WalkCols(
            WalkCols,
            r,
            2,
            x&x
        )
    )
)

MakeRowは再帰関数ではありません。指定された行の1列目を初期値にし、2列目からWalkColsを開始する入口です。

1列目を先に処理
↓
2列目からWalkColsを開始
↓
完成した1行を返す

1行目がaと1なら、MakeRow(1)は次の行を返します。

aa    11

4.4 行方向を処理するWalkRows

WalkRows,
LAMBDA(Self,r,z,
    IF(
        r>ROWS(a),
        z,
        Self(
            Self,
            r+1,
            VSTACK(z,MakeRow(r))
        )
    )
)

WalkRowsは、現在の行をMakeRow(r)で完成させ、VSTACKで結果の下へ追加します。

引数 役割
Self WalkRows自身
r 現在の行
z 完成済みの2次元配列

行番号が元配列の行数を超えたら、完成したzを返します。

4.5 1行目・1列目を初期値にする理由

列方向では、1列目を初期値にして2列目から始めます。

WalkCols(WalkCols,r,2,x&x)

行方向では、1行目を初期値にして2行目から始めます。

WalkRows(WalkRows,2,MakeRow(1))

これにより、HSTACKとVSTACKの追加先へ最初から正しい形の配列を渡せます。先頭にダミー値を置いて後から取り除く必要がありません。

4.6 2重ループの完成式

I1:J3の各セルを2回連結する完成式は次のとおりです。

=LET(
    a,I1:J3,
 
    WalkCols,
    LAMBDA(Self,r,c,z,
        IF(
            c>COLUMNS(a),
            z,
            LET(
                x,INDEX(a,r,c),
                Self(
                    Self,
                    r,
                    c+1,
                    HSTACK(z,x&x)
                )
            )
        )
    ),
 
    MakeRow,
    LAMBDA(r,
        LET(
            x,INDEX(a,r,1),
            WalkCols(
                WalkCols,
                r,
                2,
                x&x
            )
        )
    ),
 
    WalkRows,
    LAMBDA(Self,r,z,
        IF(
            r>ROWS(a),
            z,
            Self(
                Self,
                r+1,
                VSTACK(z,MakeRow(r))
            )
        )
    ),
 
    WalkRows(
        WalkRows,
        2,
        MakeRow(1)
    )
)

長い数式は、上から1文字ずつ読むのではなく、次の順序で確認します。

1. 入力範囲a
2. 列方向のWalkCols
3. 1行を作るMakeRow
4. 行方向のWalkRows
5. 最初の呼び出し

4.7 2重ループの実行順序

全体の実行順序は次のとおりです。

MakeRow(1)
  └─ WalkColsで1行目を完成
 
WalkRowsのr=2
  └─ MakeRow(2)
       └─ WalkColsで2行目を完成
  └─ VSTACKで追加
 
WalkRowsのr=3
  └─ MakeRow(3)
       └─ WalkColsで3行目を完成
  └─ VSTACKで追加
 
WalkRowsのr=4
  └─ 行数を超えたため終了

結果の変化は次のとおりです。

段階 完成結果
MakeRow(1) {aa,11}
2行目追加後 {aa,11;bb,22}
3行目追加後 {aa,11;bb,22;cc,33}

内側のWalkColsが1行を最後まで完成させ、その戻り値を外側のWalkRowsが縦へ積み重ねます。これが数式による2重ループの基本構造です。

第5章 再帰を使う場面と使わない場面

5.1 再帰が適する処理

再帰が有効なのは、単に繰り返し回数がある場合ではなく、状態や終了条件を自分で制御したい場合です。

  • 条件を満たした時点で終了する

  • 前回の結果によって次の処理が変わる

  • 処理済みの結果を次へ引き継ぐ

  • 行と列で異なる状態を管理する

  • 独自の順序で配列や階層をたどる

再帰の強みは、現在の状態を引数として明示し、そこから次の状態を作れることです。

5.2 既存関数を優先する処理

単純な配列処理では、専用関数のほうが簡潔です。

今回の各セルを2回連結する処理だけなら、MAPで書けます。

=MAP(I1:J3,LAMBDA(x,x&x))

行番号と列番号から配列を作るなら、MAKEARRAYも使えます。

=MAKEARRAY(
    ROWS(I1:J3),
    COLUMNS(I1:J3),
    LAMBDA(r,c,INDEX(I1:J3,r,c)&INDEX(I1:J3,r,c))
)
処理 優先候補
各要素へ同じ変換 MAP
行番号・列番号から配列生成 MAKEARRAY
一つの結果へ集約 REDUCE
各段階の累積結果を返す SCAN
独自の状態遷移・終了条件 再帰LAMBDA

「再帰で書ける」ことと「再帰で書くべき」ことは別です。実務では、目的を満たす最も短く、保守しやすい方法を選びます。

5.3 再帰を使用する際の注意点

終了条件がなければ処理は停止しません。終了条件があっても、状態がその条件へ近づかなければ同じです。

また、再帰呼び出しが多すぎると、Excelは#NUM!を返す場合があります。大量データでは、MAPなどの配列関数、Power Query、VBAも検討します。

長い再帰数式では、次の工夫が重要です。

  • LETで処理対象と補助関数に名前を付ける

  • 役割ごとにLAMBDAを分ける

  • 引数名の意味を統一する

  • 改行とインデントを整える

  • 状態の変化を表で確認する

1セルで完結させる教材や検証では、名前定義なしの方法が便利です。ブック内で繰り返し使う実務関数なら、名前の管理へ登録したほうが呼び出し側は簡潔になります。

Excel LabsのAdvanced Formula Environmentは、長い数式の編集や整形を補助します。ただし、再帰処理そのものはExcelのLAMBDA、LET、動的配列関数によって実行されています。

まとめ 数式で再帰を書くための設計手順

Excel数式で再帰を書くときは、完成式を一気に書かず、次の順序で設計します。

1 処理対象を決める

数値を1つ処理する
1行を処理する
1列を処理する
1セルを処理する

2 終了条件を決める

n<=1
r>ROWS(a)
c>COLUMNS(a)

3 現在の状態を引数にする

Self:関数自身
n:現在の数値
r:現在の行
c:現在の列
z:途中結果

4 次の状態を作る

n-1
r+1
c+1
VSTACK(z,新しい行)
HSTACK(z,新しい値)

Excel数式では、同じ変数を書き換えるのではなく、新しい状態を次の呼び出しへ渡します。

5 関数自身を渡す

名前定義を使わない場合は、最初の呼び出しで関数自身をSelfへ渡します。

F(F,5)

内部の再帰呼び出しでも、Selfを次へ渡します。

Self(Self,n-1)

6 初期状態から処理を開始する

F(F,5)

配列処理では、最初の要素や最初の行を初期結果として使えます。

WalkRows(WalkRows,2,MakeRow(1))

1重ループは、一つの方向へ状態を動かします。2重ループでは、「1行を完成させる列方向の処理」と「完成した行を積み重ねる行方向の処理」を組み合わせます。

WalkCols:列を進める
MakeRow:1行を完成させる
WalkRows:行を進める

数式による再帰の本質は、特殊な記法ではありません。

終了条件を決める
現在の状態を引数にする
次の状態を作る
同じ関数を再度呼び出す
途中結果を次へ渡す

この構造をLAMBDAとLETで表現すれば、Excel数式だけでも再帰処理、1重ループ、2重ループを構築できます。

参考資料

  • Microsoft Support「LAMBDA function」

  • Microsoft Support「LET function」

  • Microsoft Support「HSTACK function」

  • Microsoft Support「VSTACK function」

  • Microsoft Support「MAP function」

  • Microsoft Excel Labs「Advanced Formula Environment」

出典メモ

本記事は、ほえほえ提供のMarkdown原稿「Excel数式で学ぶ再帰処理_15ページ本文完成版.md」を基礎に、LWP公開記事として見出し階層、導入、対象読者、到達点、Word組版を整えたものです。Excel関数の参照先として、本文末尾に記載したMicrosoft Supportの各関数ページおよびMicrosoft Excel Labsを挙げています。