Fungsi lambda rekursif dalam C ++ 11

143

Saya baru mengenal C ++ 11. Saya menulis fungsi lambda rekursif berikut, tetapi tidak mengkompilasi.

sum.cpp

#include <iostream>
#include <functional>

auto term = [](int a)->int {
  return a*a;
};

auto next = [](int a)->int {
  return ++a;
};

auto sum = [term,next,&sum](int a, int b)mutable ->int {
  if(a>b)
    return 0;
  else
    return term(a) + sum(next(a),b);
};

int main(){
  std::cout<<sum(1,10)<<std::endl;
  return 0;
}

kesalahan kompilasi:

vimal @ linux-718q: ~ / Study / 09C ++ / c ++ 0x / lambda> g ++ -std = c ++ 0x sum.cpp

sum.cpp: Dalam fungsi lambda: sum.cpp: 18: 36: error: ' ((<lambda(int, int)>*)this)-><lambda(int, int)>::sum' tidak dapat digunakan sebagai fungsi

versi gcc

gcc versi 4.5.0 20091231 (percobaan) (GCC)

Tetapi jika saya mengubah deklarasi sum()seperti di bawah ini, itu berfungsi:

std::function<int(int,int)> sum = [term,next,&sum](int a, int b)->int {
   if(a>b)
     return 0;
   else
     return term(a) + sum(next(a),b);
};

Bisakah seseorang menjelaskan hal ini?

weima
sumber
Mungkinkah ini statik vs deklarasi dinamis implisit?
Hamish Grubijan
3
Apa yang dilakukan mutablekata kunci di sana?
Ceria dan hth. - Alf
Pengambilan variabel dengan durasi penyimpanan non-otomatis tidak diperbolehkan. Anda harus melakukannya dengan cara ini: chat.stackoverflow.com/transcript/message/39298544#39298544
Euri Pinhollow
Hanya FYI, dalam cuplikan kode kedua lambda Anda terlalu bertele-tele, pertimbangkan perubahan ini:std::function<int(int,int)> sum = [&](int a, int b) {
armanali

Jawaban:

189

Pikirkan tentang perbedaan antara versi otomatis dan versi tipe yang ditentukan sepenuhnya. Kata kunci otomatis menyimpulkan jenisnya dari apa pun yang diinisialisasi, tetapi apa yang Anda inisialisasi perlu tahu apa jenisnya (dalam hal ini, penutupan lambda perlu mengetahui jenis yang ditangkapnya). Sesuatu dari masalah ayam dan telur.

Di sisi lain, tipe objek fungsi yang ditentukan sepenuhnya tidak perlu "tahu" apa pun tentang apa yang ditugaskan padanya, sehingga penutupan lambda juga dapat sepenuhnya diinformasikan tentang jenis yang ditangkapnya.

Pertimbangkan sedikit modifikasi kode ini dan mungkin lebih masuk akal:

std::function<int(int,int)> sum;
sum = [term,next,&sum](int a, int b)->int {
if(a>b)
    return 0;
else
    return term(a) + sum(next(a),b);
};

Jelas, ini tidak akan berfungsi dengan otomatis . Fungsi lambda rekursif bekerja dengan sangat baik (setidaknya mereka lakukan di MSVC, di mana saya memiliki pengalaman dengan mereka), hanya saja mereka tidak benar-benar kompatibel dengan inferensi tipe.

IM McIntosh
sumber
3
Saya tidak setuju dengan ini. Jenis lambda terkenal segera setelah fungsi tubuh dimasukkan - tidak ada alasan bahwa itu tidak boleh disimpulkan saat itu.
Anak anjing
16
@ DeadMG tetapi spec melarang untuk merujuk pada autovariabel di initializer itu. jenis variabel otomatis belum diketahui saat inisialisasi sedang diproses.
Johannes Schaub - litb
1
Ingin tahu mengapa ini tidak ditandai sebagai 'jawaban', dan bahwa Python satu diklasifikasikan sebagai 'Jawab'?!
Ajay
1
@Puppy: Namun, dalam kasus penangkapan tersirat, untuk efisiensi, hanya variabel yang dirujuk yang benar-benar ditangkap, sehingga tubuh harus diuraikan.
kec
Apakah ada interpretasi yang valid untuk sumselain std::function<int(int, int)>, atau apakah spesifikasi C ++ tidak terganggu untuk menyimpulkannya?
Mateen Ulhaq
79

Caranya adalah memberi makan dalam implementasi lambda untuk dirinya sendiri sebagai parameter , bukan dengan menangkap.

const auto sum = [term,next](int a, int b) {
  auto sum_impl=[term,next](int a,int b,auto& sum_ref) mutable {
    if(a>b){
      return 0;
    }
    return term(a) + sum_ref(next(a),b,sum_ref);
  };
  return sum_impl(a,b,sum_impl);
};

Semua masalah dalam ilmu komputer dapat diselesaikan dengan tingkat tipuan lainnya . Saya pertama kali menemukan trik mudah ini di http://pedromelendez.com/blog/2015/07/16/recursive-lambdas-in-c14/

Ini tidak memerlukan C ++ 14 sementara pertanyaannya adalah pada C ++ 11, tapi mungkin menarik untuk sebagian besar.

Pergi melalui std::functionjuga dimungkinkan tetapi dapat menghasilkan kode lebih lambat. Tapi tidak selalu. Lihatlah jawaban untuk std :: function vs template


Ini bukan hanya kekhasan tentang C ++, itu langsung memetakan ke matematika kalkulus lambda. Dari Wikipedia :

Lambda calculus cannot express this as directly as some other notations:
all functions are anonymous in lambda calculus, so we can't refer to a
value which is yet to be defined, inside the lambda term defining that
same value. However, recursion can still be achieved by arranging for a
lambda expression to receive itself as its argument value
Johan Lundberg
sumber
3
Ini tampaknya jauh lebih buruk daripada menggunakan secara eksplisit function<>. Saya tidak mengerti mengapa ada orang yang lebih menyukainya. Sunting: Tampaknya lebih cepat.
Timmmm
17
ini jauh lebih baik daripada std :: berfungsi karena 3 alasan: tidak memerlukan penghapusan tipe atau alokasi memori, ini bisa berupa constexpr dan berfungsi dengan baik dengan parameter auto (templated) / tipe pengembalian
Ivan Sanz-Carasa
3
Mungkin solusi ini juga memiliki keuntungan karena dapat disalin tanpa std :: function reference keluar dari ruang lingkup?
Uri Granta
3
Hm, ketika mencoba, GCC 8.1 (linux) mengeluh: error: use of ‘[...]’ before deduction of ‘auto’- diperlukan untuk secara eksplisit menentukan jenis pengembalian (di sisi lain, tidak perlu bisa diubah-ubah).
Aconcagua
@Aconcagua sama di sini dengan Xcode10 dan saya telah menetapkan standar C ++ menjadi 17 genap
IceFire
39

Dengan C ++ 14, sekarang cukup mudah untuk membuat lambda rekursif yang efisien tanpa harus mengeluarkan biaya tambahan std::function, hanya dalam beberapa baris kode (dengan suntingan kecil dari aslinya untuk mencegah pengguna mengambil salinan yang tidak disengaja). ):

template <class F>
struct y_combinator {
    F f; // the lambda will be stored here

    // a forwarding operator():
    template <class... Args>
    decltype(auto) operator()(Args&&... args) const {
        // we pass ourselves to f, then the arguments.
        // [edit: Barry] pass in std::ref(*this) instead of *this
        return f(std::ref(*this), std::forward<Args>(args)...);
    }
};

// helper function that deduces the type of the lambda:
template <class F>
y_combinator<std::decay_t<F>> make_y_combinator(F&& f) {
    return {std::forward<F>(f)};
}

yang dengannya sumupaya awal Anda menjadi:

auto sum = make_y_combinator([term,next](auto sum, int a, int b) {
  if (a>b) {
    return 0;
  }
  else {
    return term(a) + sum(next(a),b);
  }
});

Di C ++ 17, dengan CTAD, kita dapat menambahkan panduan deduksi:

template <class F> y_combinator(F) -> y_combinator<F>;

Yang meniadakan kebutuhan akan fungsi pembantu. Kita bisa y_combinator{[](auto self, ...){...}}langsung menulis .


Dalam C ++ 20, dengan CTAD untuk agregat, panduan deduksi tidak diperlukan.

Barry
sumber
Ini bagus, tetapi orang bisa mempertimbangkan std::forward<decltype(sum)>(sum)daripada sumpada baris terakhir.
Johan Lundberg
@Johan Tidak, hanya ada satu operator()sehingga tidak ada untungnya dengan meneruskansum
Barry
Oh itu benar. Tidak terbiasa menggunakan referensi penerusan tanpa penerusan.
Johan Lundberg
Y-combinator tentu saja merupakan jalan yang harus ditempuh. Tetapi Anda benar-benar harus menambahkan non- constoverload jika fungsi-objek yang disediakan memiliki non const-panggilan-operator. Dan gunakan SFINAE dan dihitung noexceptuntuk keduanya. Juga, tidak perlu lagi fungsi pembuat di C ++ 17.
Deduplicator
2
@minex Ya, auto sumsalin ... tapi salin a reference_wrapper, yang sama dengan mengambil referensi. Melakukannya sekali dalam implementasi berarti tidak ada penggunaan yang akan secara tidak sengaja menyalin.
Barry
22

Saya punya solusi lain, tetapi hanya bekerja dengan lambdas tanpa kewarganegaraan:

void f()
{
    static int (*self)(int) = [](int i)->int { return i>0 ? self(i-1)*i : 1; };
    std::cout<<self(10);
}

Trik di sini adalah lambda dapat mengakses variabel statis dan Anda dapat mengonversi yang stateless ke penunjuk fungsi.

Anda dapat menggunakannya dengan lambda standar:

void g()
{
    int sum;
    auto rec = [&sum](int i) -> int
    {
        static int (*inner)(int&, int) = [](int& _sum, int i)->int 
        {
            _sum += i;
            return i>0 ? inner(_sum, i-1)*i : 1; 
        };
        return inner(sum, i);
    };
}

Kerjanya di GCC 4.7

Yankes
sumber
3
Ini harus memiliki kinerja yang lebih baik daripada fungsi std ::, jadi beri +1 sebagai alternatif. Tapi sungguh, pada titik ini saya bertanya-tanya apakah menggunakan lambdas adalah pilihan terbaik;)
Antoine
Jika Anda memiliki lambda yang tidak memiliki kewarganegaraan, Anda bisa membuatnya berfungsi penuh.
Timmmm
1
@Timmmm Tapi kemudian Anda membocorkan bagian implementasi ke kata luar, biasanya lambda sangat erat dengan fungsi orangtua (bahkan ketika tanpa tangkapan). Jika ini tidak terjadi maka Anda tidak harus menggunakan lambdas di tempat pertama dan menggunakan fungsi functors normal.
Yankes
10

Anda dapat membuat panggilan fungsi lambda itu sendiri secara rekursif. Satu-satunya hal yang perlu Anda lakukan adalah merujuknya melalui pembungkus fungsi sehingga kompiler tahu itu kembali dan tipe argumen (Anda tidak dapat menangkap variabel - lambda itu sendiri - yang belum didefinisikan) .

  function<int (int)> f;

  f = [&f](int x) {
    if (x == 0) return 0;
    return x + f(x-1);
  };

  printf("%d\n", f(10));

Berhati-hatilah untuk tidak kehabisan ruang pembungkus f.

Zuza
sumber
3
Tapi, ini identik dengan jawaban yang diterima, dan mungkin memiliki penalti karena menggunakan fungsi std.
Johan Lundberg
9

Untuk membuat lambda rekursif tanpa menggunakan kelas dan fungsi eksternal (seperti std::functionatau kombinator titik tetap) kita dapat menggunakan konstruksi berikut di C ++ 14 ( contoh langsung ):

#include <utility>
#include <list>
#include <memory>
#include <iostream>

int main()
{
    struct tree
    {
        int payload;
        std::list< tree > children = {}; // std::list of incomplete type is allowed
    };
    std::size_t indent = 0;
    // indication of result type here is essential
    const auto print = [&] (const auto & self, const tree & node) -> void
    {
        std::cout << std::string(indent, ' ') << node.payload << '\n';
        ++indent;
        for (const tree & t : node.children) {
            self(self, t);
        }
        --indent;
    };
    print(print, {1, {{2, {{8}}}, {3, {{5, {{7}}}, {6}}}, {4}}});
}

cetakan:

1
 2
  8
 3
  5
   7
  6
 4

Catatan, jenis hasil lambda harus ditentukan secara eksplisit.

Tomilov Anatoliy
sumber
6

Saya menjalankan benchmark membandingkan fungsi rekursif vs fungsi lambda rekursif menggunakan std::function<>metode penangkapan. Dengan optimalisasi penuh diaktifkan pada dentang versi 4.1, versi lambda berjalan lebih lambat.

#include <iostream>
#include <functional>
#include <chrono>

uint64_t sum1(int n) {
  return (n <= 1) ? 1 : n + sum1(n - 1);
}

std::function<uint64_t(int)> sum2 = [&] (int n) {
  return (n <= 1) ? 1 : n + sum2(n - 1);
};

auto const ITERATIONS = 10000;
auto const DEPTH = 100000;

template <class Func, class Input>
void benchmark(Func&& func, Input&& input) {
  auto t1 = std::chrono::high_resolution_clock::now();
  for (auto i = 0; i != ITERATIONS; ++i) {
    func(input);
  }
  auto t2 = std::chrono::high_resolution_clock::now();
  auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(t2-t1).count();
  std::cout << "Duration: " << duration << std::endl;
}

int main() {
  benchmark(sum1, DEPTH);
  benchmark(sum2, DEPTH);
}

Menghasilkan hasil:

Duration: 0 // regular function
Duration: 4027 // lambda function

(Catatan: Saya juga mengonfirmasi dengan versi yang mengambil input dari cin, sehingga menghilangkan evaluasi waktu kompilasi)

Dentang juga menghasilkan peringatan kompiler:

main.cc:10:29: warning: variable 'sum2' is uninitialized when used within its own initialization [-Wuninitialized]

Yang diharapkan, dan aman, tetapi harus diperhatikan.

Sangat menyenangkan memiliki solusi di sabuk alat kami, tetapi saya pikir bahasanya akan membutuhkan cara yang lebih baik untuk menangani kasus ini jika kinerja harus sebanding dengan metode saat ini.

catatan:

Seperti yang ditunjukkan oleh komentator, sepertinya versi terbaru VC ++ telah menemukan cara untuk mengoptimalkan ini ke titik kinerja yang sama. Mungkin kita tidak membutuhkan cara yang lebih baik untuk menangani ini (kecuali untuk gula sintaksis).

Juga, seperti beberapa pos SO lainnya telah diuraikan dalam beberapa minggu terakhir, kinerja std::function<>itu sendiri mungkin menjadi penyebab fungsi pelambatan vs panggilan secara langsung, setidaknya ketika tangkapan lambda terlalu besar untuk masuk ke dalam beberapa std::functionpenggunaan ruang perpustakaan yang dioptimalkan untuk fungsi-fungsi kecil (Saya kira agak seperti berbagai optimasi string pendek?).

mmocny
sumber
2
-1. Perhatikan bahwa satu-satunya alasan versi "lambda" memakan waktu lebih lama adalah karena Anda mengikatnya ke fungsi std ::, yang membuat operator () memanggil panggilan virtual, dan itu jelas akan memakan waktu lebih lama. Selain itu, kode Anda, dalam mode rilis VS2012, membutuhkan waktu yang sama dalam kedua kasus.
Yam Marcovic
@YamMarcovic Apa? Itu saat ini satu-satunya cara yang dikenal untuk menulis lambda rekursif (itu adalah titik contoh). Saya sangat senang mengetahui bahwa VS2012 telah menemukan cara untuk mengoptimalkan kasus penggunaan ini (meskipun ada lebih banyak perkembangan pada topik ini baru-baru ini, tampaknya jika lambda saya telah menangkap lebih banyak, itu tidak akan cocok dengan std :: function small- optimasi fungsi memori atau yang lain).
mmocny
2
Diakui. Saya salah paham posting Anda. +1 lalu. Gah, hanya bisa dihapus jika Anda mengedit jawaban ini. Jadi bisakah Anda menekankan lebih sedikit, seperti dalam komentar?
Yam Marcovic
1
@YamMarcovic Selesai. Saya menghargai kesediaan Anda untuk memberikan umpan balik dan memperbaikinya saat dibutuhkan. +1 untuk Anda, tuan yang baik.
mmocny
0 waktu biasanya berarti "seluruh operasi dioptimalkan pergi". Mengambil input dari cin tidak akan menghasilkan apa-apa jika kompiler membuktikan bahwa Anda tidak melakukan apa-apa dengan hasil komputasinya.
Yakk - Adam Nevraumont
1

Ini adalah implementasi yang sedikit lebih sederhana dari operator fixpoint yang membuatnya sedikit lebih jelas apa yang sebenarnya terjadi.

#include <iostream>
#include <functional>

using namespace std;

template<typename T, typename... Args>
struct fixpoint
{
    typedef function<T(Args...)> effective_type;
    typedef function<T(const effective_type&, Args...)> function_type;

    function_type f_nonr;

    T operator()(Args... args) const
    {
        return f_nonr(*this, args...);
    }

    fixpoint(const function_type& p_f)
        : f_nonr(p_f)
    {
    }
};


int main()
{
    auto fib_nonr = [](const function<int(int)>& f, int n) -> int
    {
        return n < 2 ? n : f(n-1) + f(n-2);
    };

    auto fib = fixpoint<int,int>(fib_nonr);

    for (int i = 0; i < 6; ++i)
    {
        cout << fib(i) << '\n';
    }
}
Nama samaran
sumber
Saya pikir Anda bisa meningkatkan jawaban Anda (kinerja bijaksana) jika Anda mengganti std::functiondengan fungsi pointer (core hanya akan bekerja dengan fungsi normal, dan lambdas stateless). Btw fib_nonrharus menerima fixpoint<int,int>, jika Anda std::functionperlu meminta salinan baru dari crating *this.
Yankes
1

Ini adalah versi yang lebih baik dari solusi Y-combinator berdasarkan pada yang diusulkan oleh @Barry.

template <class F>
struct recursive {
  F f;
  template <class... Ts>
  decltype(auto) operator()(Ts&&... ts)  const { return f(std::ref(*this), std::forward<Ts>(ts)...); }

  template <class... Ts>
  decltype(auto) operator()(Ts&&... ts)  { return f(std::ref(*this), std::forward<Ts>(ts)...); }
};

template <class F> recursive(F) -> recursive<F>;
auto const rec = [](auto f){ return recursive{std::move(f)}; };

Untuk menggunakan ini, seseorang dapat melakukan hal berikut

auto fib = rec([&](auto&& fib, int i) {
// implementation detail omitted.
});

Ini mirip dengan let reckata kunci di OCaml, meskipun tidak sama.

Ahli matematika
sumber
0

C ++ 14: Ini adalah stateless anonim rekursif / tanpa kumpulan generik lambdas yang menampilkan semua angka dari 1, 20

([](auto f, auto n, auto m) {
    f(f, n, m);
})(
    [](auto f, auto n, auto m) -> void
{
    cout << typeid(n).name() << el;
    cout << n << el;
    if (n<m)
        f(f, ++n, m);
},
    1, 20);

Jika saya mengerti benar ini menggunakan solusi Y-combinator

Dan ini adalah versi jumlah (n, m)

auto sum = [](auto n, auto m) {
    return ([](auto f, auto n, auto m) {
        int res = f(f, n, m);
        return res;
    })(
        [](auto f, auto n, auto m) -> int
        {
            if (n > m)
                return 0;
            else {
                int sum = n + f(f, n + 1, m);
                return sum;
            }
        },
        n, m); };

auto result = sum(1, 10); //result == 55
Jonas Brandel
sumber
-1

Inilah jawaban terakhir untuk OP. Bagaimanapun, Visual Studio 2010 tidak mendukung menangkap variabel global. Dan Anda tidak perlu menangkapnya karena variabel global dapat diakses secara global. Jawaban berikut menggunakan variabel lokal sebagai gantinya.

#include <functional>
#include <iostream>

template<typename T>
struct t2t
{
    typedef T t;
};

template<typename R, typename V1, typename V2>
struct fixpoint
{
    typedef std::function<R (V1, V2)> func_t;
    typedef std::function<func_t (func_t)> tfunc_t;
    typedef std::function<func_t (tfunc_t)> yfunc_t;

    class loopfunc_t {
    public:
        func_t operator()(loopfunc_t v)const {
            return func(v);
        }
        template<typename L>
        loopfunc_t(const L &l):func(l){}
        typedef V1 Parameter1_t;
        typedef V2 Parameter2_t;
    private:
        std::function<func_t (loopfunc_t)> func;
    };
    static yfunc_t fix;
};
template<typename R, typename V1, typename V2>
typename fixpoint<R, V1, V2>::yfunc_t fixpoint<R, V1, V2>::fix = [](tfunc_t f) -> func_t {
    return [f](fixpoint<R, V1, V2>::loopfunc_t x){  return f(x(x)); }
    ([f](fixpoint<R, V1, V2>::loopfunc_t x) -> fixpoint<R, V1, V2>::func_t{
        auto &ff = f;
        return [ff, x](t2t<decltype(x)>::t::Parameter1_t v1, 
            t2t<decltype(x)>::t::Parameter1_t v2){
            return ff(x(x))(v1, v2);
        }; 
    });
};

int _tmain(int argc, _TCHAR* argv[])
{
    auto term = [](int a)->int {
      return a*a;
    };

    auto next = [](int a)->int {
      return ++a;
    };

    auto sum = fixpoint<int, int, int>::fix(
    [term,next](std::function<int (int, int)> sum1) -> std::function<int (int, int)>{
        auto &term1 = term;
        auto &next1 = next;
        return [term1, next1, sum1](int a, int b)mutable ->int {
            if(a>b)
                return 0;
        else
            return term1(a) + sum1(next1(a),b);
        };
    });

    std::cout<<sum(1,10)<<std::endl; //385

    return 0;
}
Mesin Tanah
sumber
Apakah mungkin membuat kompiler jawaban ini agnostik?
rayryeng
-2

Anda mencoba menangkap variabel (jumlah) yang sedang Anda tentukan. Itu tidak baik.

Saya tidak berpikir benar-benar self-recursive C ++ 0x lambdas adalah mungkin. Anda harus bisa menangkap lambda lain.

Terry Mahaffey
sumber
3
tetapi itu berfungsi jika pernyataan penjumlahan diubah dari 'otomatis' ke std :: function <int (int, int)> tanpa mengubah daftar tangkap.
weima
Karena itu bukan lagi lambda, tetapi fungsi yang dapat digunakan sebagai pengganti lambda?
Hamish Grubijan
-2

Jawaban ini lebih rendah daripada jawaban Yankes, tetapi tetap saja, inilah jawabannya:

using dp_type = void (*)();

using fp_type = void (*)(dp_type, unsigned, unsigned);

fp_type fp = [](dp_type dp, unsigned const a, unsigned const b) {
  ::std::cout << a << ::std::endl;
  return reinterpret_cast<fp_type>(dp)(dp, b, a + b);
};

fp(reinterpret_cast<dp_type>(fp), 0, 1);
pengguna1095108
sumber
Saya pikir Anda harus menghindari reinterpret_cast. Mungkin cara terbaik dalam kasus Anda adalah membuat beberapa struct yang menggantikan dp_type. Itu harus memiliki lapangan fp_type, dapat dibangun dari fp_typedan memiliki operator ()dengan argumen seperti fp_type. Ini akan dekat dengan std::functiontetapi akan memungkinkan argumen referensi diri.
Yankes
Saya ingin memposting contoh minimal, tanpa struct, merasa bebas untuk mengedit jawaban saya dan memberikan solusi yang lebih lengkap. A structjuga akan menambah tingkat tipuan tambahan. Contohnya bekerja dan para pemainnya memenuhi standar, saya tidak tahu untuk apa -1itu.
user1095108
tidak, struct hanya akan berfungsi sebagai wadah untuk penunjuk dan akan diteruskan sebagai nilai. Ini tidak lebih dari tipuan atau overhead daripada pointer. Dan tentang -1saya tidak tahu siapa yang memberikannya kepada Anda, tapi saya pikir itu karena reinterpret_castharus digunakan sebagai pilihan terakhir.
Yankes
The castdiduga dijamin untuk bekerja dengan c ++ 11 standar. Menggunakan struct, di mata saya, bisa mengalahkan penggunaan objek lambda. Bagaimanapun, structAnda mengusulkan adalah functor, memanfaatkan objek lambda.
user1095108
Lihatlah solusi @Pseudonym, hapus saja std::functiondan Anda akan memiliki sesuatu yang dekat dengan yang ada dalam pikiran saya. Ini mungkin akan memiliki kinerja yang mirip dengan solusi Anda.
Yankes
-3

Anda memerlukan kombinator titik tetap. Lihat ini .

atau lihat kode berikut:

//As decltype(variable)::member_name is invalid currently, 
//the following template is a workaround.
//Usage: t2t<decltype(variable)>::t::member_name
template<typename T>
struct t2t
{
    typedef T t;
};

template<typename R, typename V>
struct fixpoint
{
    typedef std::function<R (V)> func_t;
    typedef std::function<func_t (func_t)> tfunc_t;
    typedef std::function<func_t (tfunc_t)> yfunc_t;

    class loopfunc_t {
    public:
        func_t operator()(loopfunc_t v)const {
            return func(v);
        }
        template<typename L>
        loopfunc_t(const L &l):func(l){}
        typedef V Parameter_t;
    private:
        std::function<func_t (loopfunc_t)> func;
    };
    static yfunc_t fix;
};
template<typename R, typename V>
typename fixpoint<R, V>::yfunc_t fixpoint<R, V>::fix = 
[](fixpoint<R, V>::tfunc_t f) -> fixpoint<R, V>::func_t {
    fixpoint<R, V>::loopfunc_t l = [f](fixpoint<R, V>::loopfunc_t x) ->
        fixpoint<R, V>::func_t{
            //f cannot be captured since it is not a local variable
            //of this scope. We need a new reference to it.
            auto &ff = f;
            //We need struct t2t because template parameter
            //V is not accessable in this level.
            return [ff, x](t2t<decltype(x)>::t::Parameter_t v){
                return ff(x(x))(v); 
            };
        }; 
        return l(l);
    };

int _tmain(int argc, _TCHAR* argv[])
{
    int v = 0;
    std::function<int (int)> fac = 
    fixpoint<int, int>::fix([](std::function<int (int)> f)
        -> std::function<int (int)>{
        return [f](int i) -> int{
            if(i==0) return 1;
            else return i * f(i-1);
        };
    });

    int i = fac(10);
    std::cout << i; //3628800
    return 0;
}
Mesin Tanah
sumber