LoginSignup
0
1

More than 3 years have passed since last update.

C++のラムダ式で再帰呼び出し[追記:処理系依存のため非推奨]

Last updated at Posted at 2021-01-19

(追記:2021/01/19 20:51) 注意!!!

当記事のラムダ式の再帰処理は特定の環境でしか再現できないことがわかりました。
再現できる環境であっても移植性の問題や修正される可能性もありますので、お勧めしません
コメント欄にてご指摘くださった@kazatsuyu様、ありがとうございました。

ラムダ式の再帰処理については、先駆者様の記事 C++ : ラムダ式のみでの再帰文 - Qiita 等をご覧ください。

[追記:2021/01/19 23:33]
他所様の記事に丸投げなのもどうかと思ったので、Wandbox(gcc, clang)で動作するコードを置いておきます。(要C++14以上)
先駆者様の記事やコメントで触れられていた、第一引数に自分自身を渡す手法です。やっぱり素直に実装するのが一番ですね...。

#include <iostream>

auto factrial = [](auto f, size_t x) -> size_t {
    return x <= 1 ? 1 : x * f(f, x - 1);
};

int main(int, char**)
{
    std::cout << factrial(factrial, 10) << std::endl;
    return 0;
}

(以下、元記事)

はじめに

C++の勉強中に、ふと「ラムダ式で再帰呼び出しってどうするんだ?」と思ったので、やり方を調べてみました。
この記事では、とりあえず再帰関数の王道(?)である自然数の階乗を計算するラムダ式を実装してみることにします。

  • 環境
    • Windows 10 Home
    • Microsoft Visual Studio Community 2019 (std:c++latest)

まずは普通の関数で書いてみる

size_t factrial(size_t x) {
    return x <= 1 ? 1 : x * factrial(--x);
}

書き方は色々あると思いますが、今回は条件演算子を用いることにしました。
Wikipediaによると0! = 1らしいので、これで一応は問題ないかと思います。

ラムダ式で書いてみる

次は先ほどの関数をラムダ式に書き直してみます。とりあえず直感的に書いたのが以下のコードです。

int main(int, char**) {
    auto factrial = [](size_t x) -> size_t {
        return x <= 1 ? 1 : x * factrial(--x);  //Error!
    };

    auto result = factrial(10);
}

しかし、このコードではコンパイルを通りません。
原因は、ラムダ式内でfactrial(size_t)を呼び出そうとしてしまっていることです。
ここでは、ラムダ式が関数オブジェクトの定義であり、factrialがその関数オブジェクト型の一時変数ですから、ラムダ式内でfactrialが呼び出せないのは、よくよく考えれば当然のことでした。

では、ラムダ式の中で自身を呼び出すためには、どのように記述したら良いでしょうか。
ここで、そもそもC++におけるラムダ式とは何か?ということをcpprefjpで確認してみると、その中に以下のようなことが書いてありました。

ここでは、[](int a, int b) { return a + b; }というコードがラムダ式に当たる。(中略)
このラムダ式によって、その場に以下のような関数オブジェクトが定義される:

struct F {
  auto operator()(int a, int b) const -> decltype(a + b)
  {
      return a + b;
  }
};

つまり、ラムダ式はoperator()をオーバーロードした関数オブジェクトを作る糖衣構文と考えてよさそうです。
ということは、先ほどのコードは以下のように書き換えられます。

int main(int, char**) {
    auto factrial = [](size_t x) -> size_t {
        return x <= 1 ? 1 : x * operator()(--x);
    };

    auto result = factrial(10);
}

自身を呼び出す箇所でoperator()(size_t)を呼び出しています。
このコードであればコンパイルも通り、ラムダ式による再帰処理が実装できるようです。
ただ、自分で書く分にはいいですが、他人のコードを見ているときに急にoperator()が出てくると「何だこれ?」となりそうだなとも思いますが...。

おわりに

ラムダ式の再帰処理については、C++ : ラムダ式のみでの再帰文 - Qiitaくらいしか関連記事が見つけられず、そちらでもoperator()については触れられていなかったので、初めての投稿のテーマにしてみました。
記事内に間違い等あれば、コメント頂けると幸いです。

0
1
2

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
0
1