HOWTO · C++

C++ の再帰ラムダ関数

この記事では、C++ プログラミング言語の再帰ラムダ関数を理解します。

この記事では、C++ で使用できる再帰ラムダについて理解します。

C++ 再帰ラムダ関数

再帰とは、関数が(直接的または間接的に)自分自身を呼び出すプロセスを指し、このプロセスに対応する関数は再帰関数と呼ばれます。 再帰ラムダ式は、このプロセスの結果です。

再帰的な方法を使用していくつかの問題にアプローチすると、解決がはるかに簡単になります。

ローカル コードを再構築する過程で、ラムダは便利なツールです。 一方、ラムダを内部から利用したい場合もあります。

これは、直接再帰を有効にするため、またはクロージャーを継続として登録できるようにするための 2つの理由のいずれかで行われます。 現在のバージョンの C++ では、このタスクを成功させるのは非常に困難です。

C++ での再帰 Lambda 関数の実装

最初のステップでは、必要なライブラリをインポートします。

#include <functional>
#include <iostream>

main() 関数内で、int 型の変数を初期化し、整数値を割り当てます。

int main() { int anyNumber = 456789; }

次に、void 型の関数を作成し、reverseByRecursiveLambda という名前を付けます。

function<void()> reverseByRecursiveLambda;

次に、再帰ラムダ関数で reverseByRecursiveLambda 関数を初期化します。

reverseByRecursiveLambda = [&]() {};

このメソッド内で、anyNumber の整数値が 0 に等しい場合は何も返さないというチェックを実装する必要があります。 そうでない場合、関数は次の一連の操作に進む必要があります。

次に、関数は一連の数学的計算を実行した後、結果を出力します。 最後のステップとして、reverseByRecursiveLambda() という名前の関数を再度実行します。

if (anyNumber == 0) return;

cout << anyNumber % 10 << " ";
anyNumber /= 10;
reverseByRecursiveLambda();

この関数の実行が完了したら、必要なのは main() メソッド内でこの関数を呼び出すことだけです。

int main() { reverseByRecursiveLambda(); }

完全なソース コード:

#include <functional>
#include <iostream>

using namespace std;

int main() {
  int anyNumber = 123456789;

  function<void()> reverseByRecursiveLambda;

  reverseByRecursiveLambda = [&]() {
    if (anyNumber == 0) return;

    cout << anyNumber % 10 << " ";
    anyNumber /= 10;
    reverseByRecursiveLambda();
  };

  reverseByRecursiveLambda();
}

出力:

9 8 7 6 5 4 3 2 1

std::function を使用した C++ 再帰ラムダ関数

std::function クラス テンプレートは、さまざまな目的に使用できるポリモーフィック関数ラッパーです。 ラムダ式は式をバインドします。または、他の関数オブジェクトを std::function クラスのインスタンス内に格納、コピー、および呼び出すことができます。

さらに、これらのインスタンスは、メンバー関数へのポインターとデータ メンバーへのポインターを保持できます。

次の例では、階乗のスナップショットを取得する必要があります。その後、ラムダ本体内で参照できます。

コード例:

#include <functional>
#include <iostream>

int main() {
  const std::function<int(int)> factorial = [&factorial](int n) {
    return n > 1 ? n * factorial(n - 1) : 1;
  };

  std::cout << "The factorial of 5 is: " << factorial(5);
  return factorial(5);
}

出力:

The factorial of 5 is: 120

ジェネリック ラムダ を使用した C++ 再帰ラムダ関数

ジェネリック ラムダ式は、auto 型の引数を少なくとも 1つ持つラムダ式を参照します。

このオプションを可能にした汎用ラムダ式のおかげで、再帰ラムダ関数を作成するために std::function を使用する必要はありません。 ラムダ式の型は、その名前だけでは判断できません。

そのため、C++14 がリリースされる前は、再帰ラムダ式を std::function で囲む必要がありました。 std::function に依存せず、数値ベース型で適切に実行するためにデータをキャプチャする必要はありません。

この例では、汎用ラムダ auto を使用して map に格納されたデータを表示する方法を示します。

コード例:

#include <algorithm>
#include <iostream>
#include <map>
#include <string>

int main() {
  const std::map<std::string, int> numbers{
      {"Saad", 22}, {"Zaryab", 23}, {"Zeeshan", 24}};

  std::for_each(std::begin(numbers), std::end(numbers), [](const auto& entry) {
    std::cout << "Name: " << entry.first << "   Age: " << entry.second << '\n';
  });
}

GCC 11.1.0 は、このデモで使用されているコンパイラの名前です。

出力:

Name: Saad   Age: 22
Name: Zaryab   Age: 23
Name: Zeeshan   Age: 24

y_combinator を使用した C++ 再帰ラムダ関数

無名関数 (ラムダとも呼ばれます) は、ロジックが単純で、名前付き関数に抽出する必要がある場合にのみ使用することを意図しているため、ラムダ内で再帰呼び出しが行われる可能性は低いです。

しかし、再帰を利用できないとしましょう。 関数が私たちの言語で第一級市民として扱われる限り (関数は変数に割り当てられ、引数として提供され、他のオブジェクトと同様に返される可能性があります)、再帰の実装を書くことができます。

Y コンビネータ は、この目的に役立つ高階関数です。 その不吉な響きの名前にもかかわらず、これは単なる高階関数であり、別の関数をラップする関数です。

以下は、y コンビネータ を使用してフィボナッチ数列をリストする再帰ラムダ関数を作成する例です。

コード例:

#include <iostream>

template <typename Function>
struct YCombinator {
  Function yFunction;

  template <typename... Args>
  decltype(auto) operator()(Args&&... args) {
    return yFunction(*this, std::forward<Args>(args)...);
  }
};

template <typename Function>
decltype(auto) make_YCombinator(Function f) {
  return YCombinator<Function>{f};
}

int main() {
  auto fib = make_YCombinator([](auto self, int n) {
    if (n < 2) return 1;
    return self(n - 1) + self(n - 2);
  });

  for (int i = 0; i < 10; ++i)
    std::cout << "fib(" << i << ") = " << fib(i) << "\n";

  return 0;
}

GCC 11.1.0 は、このデモで使用されているコンパイラの名前です。

出力:

fib(0) = 1
fib(1) = 1
fib(2) = 2
fib(3) = 3
fib(4) = 5
fib(5) = 8
fib(6) = 13
fib(7) = 21
fib(8) = 34
fib(9) = 55