C ++ 11'de özyinelemeli lambda işlevleri


143

C ++ 11 için yeniyim. Aşağıdaki özyinelemeli lambda işlevini yazıyorum, ancak derlemiyor.

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;
}

Derleme Hatası:

vimal @ linux-718q: ~ / Çalışma / 09C ++ / c ++ 0x / lambda> g ++ -std = c ++ 0x sum.cpp

sum.cpp: lambda fonksiyonunda: sum.cpp: 18: 36: hata: ' ((<lambda(int, int)>*)this)-><lambda(int, int)>::sum' fonksiyon olarak kullanılamaz

gcc sürümü

gcc sürüm 4.5.0 20091231 (deneysel) (GCC)

Ancak sum()aşağıdaki beyanı değiştirirsem işe yarar :

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);
};

Birisi buna ışık tutabilir mi lütfen?


Bu, statik ve dolaylı olarak dinamik bildirimler olabilir mi?
Hamish Grubijan

3
mutableAnahtar kelime orada ne yapıyor?
Şerefe ve s. - Alf

Otomatik olmayan depolama süresine sahip değişkenlerin yakalanmasına izin verilmez. Bunu şu şekilde yapmalısınız
Euri Pinhollow

Sadece bir FYI, ikinci kod snippet'inde lambda çok ayrıntılı, bu değişikliği düşünün:std::function<int(int,int)> sum = [&](int a, int b) {
armanali

Yanıtlar:


189

Otomatik sürüm ile tam olarak belirtilen tür sürümü arasındaki farkı düşünün . Oto anahtar kelime infers onun o başlatılır ne olursa olsun gelen türünü, ama ne onun tipi olduğunu bilmek ihtiyaçları ile başlatılıyor ediyoruz (bu durumda, lambda kapatma ihtiyacı o yakalayan oluyor türlerini bilmek). Tavuk ve yumurta problemi.

Öte yandan, tam olarak belirlenmiş bir fonksiyon nesnesinin tipinin kendisine atanan şey hakkında "bilmesi" gerekmez ve bu nedenle lambda'nın kapatılması da yakaladığı tipler hakkında tam olarak bilgilendirilebilir.

Kodunuzdaki bu küçük değişikliği düşünün ve daha mantıklı olabilir:

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);
};

Açıkçası, bu otomatik ile işe yaramaz . Özyinelemeli lambda işlevleri mükemmel çalışır (en azından onlarla deneyime sahip olduğum MSVC'de yaparlar), sadece tür çıkarımıyla gerçekten uyumlu olmamalarıdır.


3
Buna katılmıyorum. Lambda türü, işlev gövdesine girilir girilmez iyi bilinir - o zamana kadar çıkarılmaması için bir neden yoktur.
Köpek yavrusu

16
@DeadMG ancak spec auto, başlatıcısındaki değişkene atıfta bulunmayı yasaklar . başlatıcı işlenirken otomatik değişkenin türü henüz bilinmemektedir.
Johannes Schaub - litb

1
Bunun neden 'cevap' olarak işaretlenmediğini ve Python'un 'Cevap' olarak sınıflandırıldığını mı merak ediyorsunuz?
Ajay

1
@Puppy: Örtük bir yakalama durumunda, verimlilik için yalnızca referans alınan değişkenler gerçekten yakalanır, bu nedenle gövde ayrıştırılmalıdır.
kec

Bunun sumdışında geçerli bir yorum var mı std::function<int(int, int)>, yoksa C ++ spesifikasyonu onu çıkarmak için uğraşmadı mı?
Mateen Ulhaq

79

İşin püf noktası, lambda uygulamasında yakalama yoluyla değil, kendisine bir parametre olarak beslemektir .

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);
};

Bilgisayar bilimindeki tüm problemler başka bir dolaylama seviyesi ile çözülebilir . Bu kolay numarayı önce http://pedromelendez.com/blog/2015/07/16/recursive-lambdas-in-c14/ adresinde buldum

Bu vermez soru C ++ 11 açıkken C ++ 14 gerektirmez, ancak çoğu belki ilginç.

Yoluyla gitmek std::functionde mümkündür ama olabilir yavaş kod ile sonuçlanır. Ama her zaman değil. Std :: function vs template yanıtlarına bir göz atın


Bu sadece C ++ ile ilgili bir özellik değil, doğrudan lambda hesabının matematiği ile eşleşiyor. Gönderen Vikipedi :

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

3
Bu açıkça kullanmaktan çok daha kötü görünüyor function<>. Neden kimsenin bunu tercih ettiğini anlayamıyorum. Düzenleme: Görünüşe göre daha hızlı.
Timmmm

17
Bu, std :: fonksiyonundan 3 nedenden daha iyi: tip silme veya bellek ayırma gerektirmez, constexpr olabilir ve otomatik (tempüle) parametreler / dönüş tipi ile düzgün çalışır
Ivan Sanz-Carasa

3
Muhtemelen bu çözüm, std :: fonksiyon referansı kapsam dışına çıkmadan da kopyalanabilme avantajına sahiptir?
Uri Granta

3
Hm, denerken, GCC 8.1 (linux) şikayet etti: error: use of ‘[...]’ before deduction of ‘auto’- dönüş türünü açıkça belirtmek için gerekli (diğer taraftan, değiştirilebilir olması gerekmiyordu).
Aconcagua

@Aconcagua burada Xcode10 ile aynı ve C ++ standardını 17'ye ayarladım
IceFire

39

C ++ 14 ile, std::functionyalnızca birkaç satır satırda ek bir ek yüke maruz kalmadan verimli bir özyinelemeli lambda yapmak artık çok kolaydır (kullanıcının yanlışlıkla kopyasını almasını önlemek için orijinalden küçük bir düzenleme ile) ):

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)};
}

Orijinal sumgirişiminiz şu şekilde olur:

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);
  }
});

C ++ 17'de CTAD ile bir kesinti kılavuzu ekleyebiliriz:

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

Hangi yardımcı fonksiyon ihtiyacını ortadan kaldırır. Sadece y_combinator{[](auto self, ...){...}}doğrudan yazabiliriz .


C ++ 20'de, toplamalar için CTAD ile, kesinti kılavuzu gerekli olmayacaktır.


Bu harika, ama son satır std::forward<decltype(sum)>(sum)yerine düşünebilirsiniz sum.
Johan Lundberg

@Johan Hayır, sadece bir tane var, operator()bu yüzden ileterek kazanacak bir şey yoksum
Barry

Oh, bu doğru. İletme olmadan iletme referanslarını kullanmaya alışık değildir.
Johan Lundberg

Y-birleştiricisi kesinlikle gidilecek yoldur. Ancak const, sağlanan işlev nesnesinde constçağrı yapmayan bir işleç varsa, aşırı yüklenmemesi gerekir . Ve SFINAE kullanın ve noexcepther ikisi için hesaplandı . Ayrıca, artık C ++ 17'de yapıcı işlevine gerek yok.
Tekilleştirici

2
@ minex Evet, auto sumkopyalar ... ama a kopyalar reference_wrapper, bu da referans almakla aynı şeydir. Uygulamada bir kez yapılması, kullanımların hiçbirinin yanlışlıkla kopyalanmayacağı anlamına gelir.
Barry

22

Başka bir çözümüm var, ancak sadece vatansız lambdalarla çalışıyorum:

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

Buradaki numara, lambdasların statik değişkenlere erişebilmesidir ve vatansız olanları işlev işaretçisine dönüştürebilirsiniz.

Standart lambdas ile kullanabilirsiniz:

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);
    };
}

GCC 4.7'deki çalışmaları


3
Bu, std :: fonksiyonundan daha iyi performansa sahip olmalıdır, bu yüzden alternatif için +1. Ama gerçekten, bu noktada lambdas kullanmanın en iyi seçenek olup olmadığını merak ediyorum;)
Antoine

Vatansız bir lambda'nız varsa, tam işlevini yerine getirebilirsiniz.
Timmmm

1
@Timmmm Ama sonra uygulamanın bir kısmını dış kelimeye sızdırıyorsunuz, genellikle lambdalar ebeveyn işleviyle (dışarı yakalamalarda bile) yakından bağlantılıdır. Eğer durum böyle değilse, ilk etapta lambdas kullanmamalı ve functorların normal işlevlerini kullanmamalısınız.
Yankes

10

Sen edebilir bir lambda fonksiyonu kendisini özyinelemeli olarak arama yapın. Yapmanız gereken tek şey, derleyicinin dönüş ve bağımsız değişken türünü bilmesi için bir işlev sarmalayıcı aracılığıyla başvurmaktır (henüz tanımlanmamış bir değişkeni - lambda'nın kendisi - yakalayamazsınız). .

  function<int (int)> f;

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

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

Sargının kapsamı dışına çıkmamaya çok dikkat edin f.


3
Ancak, bu kabul edilen cevapla aynıdır ve std işlevini kullanma cezası olabilir.
Johan Lundberg

9

Harici sınıflar ve işlevler ( std::functionsabit nokta birleştirici gibi) kullanmadan lambda yinelemeli hale getirmek için C ++ 14'te aşağıdaki yapı kullanılabilir ( canlı örnek ):

#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}}});
}

baskılar:

1
 2
  8
 3
  5
   7
  6
 4

Dikkat edin, lambda'nın sonuç türü açıkça belirtilmelidir.


6

std::function<>Yakalama yöntemini kullanarak özyinelemeli bir işlevi özyinelemeli bir lambda işleviyle karşılaştıran bir ölçüt çalıştırdım . Clang sürüm 4.1'de tam optimizasyonlar etkinleştirildiğinde, lambda sürümü önemli ölçüde yavaşladı.

#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);
}

Sonuç verir:

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

(Not: Derleme zamanı değerlendirmesini ortadan kaldırmak için cin'ten girdileri alan bir sürümle de onayladım)

Clang ayrıca bir derleyici uyarısı üretir:

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

Bu beklenen ve güvenli, ancak not edilmelidir.

Alet kayışlarımızda bir çözüme sahip olmak harika, ancak performansın mevcut yöntemlerle karşılaştırılabilir olması durumunda, dilin bu durumu ele almak için daha iyi bir yola ihtiyacı olacağını düşünüyorum.

Not:

Bir yorumcunun işaret ettiği gibi, VC ++ 'nın son sürümü bunu eşit performans noktasına göre optimize etmenin bir yolunu bulmuş gibi görünüyor. Belki de bununla başa çıkmanın daha iyi bir yoluna ihtiyacımız yok, sonuçta (sözdizimsel şeker hariç).

Ayrıca, diğer bazı SO yayınları son haftalarda ana hatlarıyla belirtildiği gibi std::function<>, en azından lambda yakalama, std::functionküçük functors için kitaplık için optimize edilmiş alan kullanımlarına sığmayacak kadar büyük olduğunda, performansın doğrudan arama işlevine karşı yavaşlamasının nedeni olabilir. (Sanırım çeşitli kısa dize optimizasyonları gibi?).


2
-1. "Lambda" sürümünün daha uzun sürmesinin tek sebebinin, onu std :: işlevine bağlamanıza dikkat edin, bu da operatörün () sanal aramayı çağırmasını sağlar ve bu daha uzun sürer. Bunun da ötesinde, VS2012 yayın modunda kodunuz her iki durumda da yaklaşık aynı süreyi aldı.
Yam Marcovic

@YamMarcovic Ne? Şu anda yinelemeli bir lambda yazmanın bilinen tek yolu bu (örneğin amacı buydu). VS2012'nin bu kullanım durumunu optimize etmenin bir yolunu bulduğunu bilmekten çok memnunum (son zamanlarda bu konuda daha fazla gelişme olmasına rağmen, görünüşe göre lambda daha fazla yakalamış olsaydı, std :: function small- bellek functor optimizasyonları veya ne değilse).
mmocny

2
Anlaşıldı. Gönderinizi yanlış anladım. +1 sonra. Gah, ancak bu cevabı düzenlerseniz oyunuzu yükseltebilirsiniz. Peki yorumda olduğu gibi biraz daha vurgulayabilir misiniz?
Yam Marcovic

1
@YamMarcovic Tamamlandı. Geri bildirim sağlama ve gerektiğinde hassaslaştırma isteğiniz için teşekkür ederiz. Sana +1, efendim.
mmocny

0 kez genellikle "tüm işlem optimize edildi" anlamına gelir. Derleyici, hesaplamanızın sonucuyla hiçbir şey yapmadığınızı kanıtlarsa cin'den girdi almak hiçbir şey yapmaz.
Yakk - Adam Nevraumont

1

Bu, düzeltme noktası operatörünün neler olduğunu biraz daha açık hale getiren biraz daha basit bir uygulamasıdır.

#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';
    }
}

std::functionİşlev işaretçisi (çekirdeklerin yalnızca normal işlev ve vatansız lambdalarla çalışacak) ile değiştirirseniz yanıtınızı (performans açısından akıllıca) geliştirebileceğinizi düşünüyorum . Btw , yeni kopyasını gerektiren bir kopyasını kullanırsanız fib_nonrkabul etmelidir . fixpoint<int,int>std::function*this
Mart'ta Yankes

1

@Barry tarafından önerilene dayanan Y-birleştirici çözümünün rafine bir sürümü.

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)}; };

Bunu kullanmak için aşağıdakiler yapılabilir

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

Her let recne kadar aynı olmasa da, OCaml'deki anahtar kelimeye benzer .


0

C ++ 14: Burada özyinelemeli anonimsiz vatansız / 1, 20'den tüm sayıların çıktısını alan genel bir lambda yakalama seti yok

([](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);

Doğru anlıyorsam bu Y-birleştirici çözümünü kullanıyor

Ve işte toplam (n, m) versiyonu

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

-1

İşte OP için son cevap. Her neyse, Visual Studio 2010 global değişkenlerin yakalanmasını desteklemez. Ve bunları yakalamanıza gerek yok çünkü global değişken tanımla global olarak erişilebilir. Aşağıdaki yanıt bunun yerine yerel değişkeni kullanır.

#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;
}

Bu cevap derleyicisini agnostik yapmak mümkün mü?
rayryeng

-2

Tanımlamanın ortasında olduğunuz bir değişkeni (toplamı) yakalamaya çalışıyorsunuz. Bu iyi olamaz.

Gerçekten özyinelemeli C ++ 0x lambdas mümkün olduğunu düşünmüyorum. Yine de diğer lambdasları yakalayabilmelisin.


3
ancak toplama bildirimi yakalama listesini değiştirmeden 'auto' dan std :: function <int (int, int)> olarak değiştirilirse çalışır.
weima

O zaman artık bir lambda değil, lambda yerine kullanılabilecek bir işlev mi?
Hamish Grubijan

-2

Bu cevap Yankes'in cevabından daha aşağıdır, ama yine de işte gidiyor:

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);

Bence kaçınmalısın reinterpret_cast. Muhtemelen en iyi yolu yerine bazı yapı oluşturmaktır dp_type. Alanı olmalı, bu alandan fp_typeoluşturulabilir fp_typeve operatör ()gibi argümanlara sahip olmalıdır fp_type. Bu yakın olacak, std::functionancak kendi kendini referanslama argümanına izin verecektir.
Yankes

Bir yapı olmadan, cevabımı düzenlemek ve daha eksiksiz bir çözüm sunmaktan çekinmeyin, minimal bir örnek göndermek istedim. A structayrıca ilave bir dolaylama seviyesi de ekler. Örnek çalışıyor ve oyuncular standartlara uygun, bunun ne için -1olduğunu bilmiyorum .
user1095108

hayır, struct yalnızca işaretçi için konteyner olarak çalışır ve değer olarak iletilir. Bu, işaretçiden daha fazla dolaylı veya ek yük olmayacaktır. Ve -1sana kimin verdiğini bilmiyordum, ama sanırım çünkü reinterpret_castson çare olarak kullanılmalı.
Yankes

castSözde c ++ 11 standardına göre iş için garanti edilir. Bir Kullanılması structgözlerimde, bir lambda nesnesinin kullanımını yenmek başladı. Sonuçta, structönerdiğiniz bir lambda nesnesini kullanan bir işlev.
user1095108

@Pseudonym çözümüne bakın, yalnızca kaldırın ve aklımda olana std::functionyakın bir şey olacak. Bu muhtemelen çözümünüzle benzer performansa sahip olacaktır.
Mart'ta Yankes

-3

Sabit nokta birleştiricisine ihtiyacınız var. Bkz bu .

veya aşağıdaki koda bakın:

//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;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.