2013-03-11 13 views
7

Navigando un po 'di bordo internet ho incontrato questa piccola sfida:chiamata lambda senza legarsi ad un identificatore

"implementare una funzione anonima ricorsiva nella tua lingua preferita"

Ovviamente questo è facile utilizzando un std :: pointer funzione/funzione.

Ciò che mi interessa veramente è se questo è possibile senza vincolare il lambda a un identificatore?

Qualcosa di simile (ignorando l'ovvio ricorsione infinita):

[](){ this(); }(); 
+1

Utilizzare il [combinatore di punti fissi] (http://stackoverflow.com/questions/152084/fixed-point-combinators-in-c) e si può trasformare qualsiasi funzione in ricorsiva. – didierc

+1

Non volevi identificatore, o intendevi davvero anonimo?C++ può avere nomi non anonimi che sono _composti di_identificatori ma non sono essi stessi identificatori. –

+0

Per nessun identificatore intendo nessun nome variabile – user1233963

risposta

5

Naturalmente, in C++, per chiamare qualsiasi funzione dovete associarlo a un identificatore da qualche parte, semplicemente a causa di vincoli di sintassi. Ma, se si accettano parametri non sufficientemente definiti, è possibile creare una versione del combinatore y in C++ che ricorre in modo piacevole senza essere "denominato".

Ora, questo è davvero brutto perché non so come fare un typedef for a recursive lambda. Quindi usa solo un sacco di abuso di cast. Funziona e stampa FLY!! fino a segoults a causa dello stack overflow.

#include <iostream> 

typedef void(*f0)(); 
typedef void(*f)(f0); 

int main() { 
    [](f x) { 
     x((f0)x); 
    } ([](f0 x) { 
     std::cout<<"FLY!!\n"; 
     ((f)x)(x); 
    }); 
} 

I due lambda sono senza nome, nel senso che nessuno dei due è esplicitamente assegnato al nome. Il secondo lambda è il vero cavallo di battaglia, e sostanzialmente si chiama usando il primo lambda per ottenere un riferimento a se stesso nella forma del parametro.

Ecco come si potrebbe usare questo per fare un po ' "utile" di lavoro:

#include <iostream> 

typedef int param_t; 
typedef int ret_t; 

typedef void(*f0)(); 
typedef ret_t(*f)(f0, param_t); 

int main() { 
    /* Compute factorial recursively */ 
    std::cout << [](f x, param_t y) { 
     return x((f0)x, y); 
    } ([](f0 x, param_t y) { 
     if(y == 0) 
      return 1; 
     return y*((f)x)(x, y-1); 
    }, 10) << std::endl; 
} 
+0

Perché il downvote? questo è bellissimo! – user1233963

3

È consentito barare?

void f(){ 
    []{ f(); }(); 
} 

È ricorsivo, indirettamente, almeno.

Altrimenti no, non c'è modo di riferirsi al lambda stesso senza assegnargli un nome.

+1

Soluzione interessante, ma il nome di una funzione è anche un identificatore, giusto? – user1233963

+2

@user: certo, ma la domanda non ha vietato una ricorsione indiretta. :) – Xeo

+2

@Xeo: equivale a "void f() {f(); } ', davvero ... – nneonneo

1

Nessun identificatore per funzioni/metodi, Abbastanza vicino o no!?

struct A 
{ 
    void operator()() 
    { 
     [&]() 
     { 
      (*this)(); 
     }(); 
    } 
}; 

Per chiamare

A{}(); // Thanks MooningDuck 
+3

Fondamentalmente è lo stesso della mia risposta, un indiretto tramite una funzione con nome. Inoltre, WTF 'nuovo A'. – Xeo

+1

Ho provato a sbarazzarmi di 'f' sovraccaricando'() '. :-) – deepmax

+0

@MM. È possibile evitare il WTF rendendo l'operatore statico, o (in C++ 11) 'A {}()'. Inoltre, il "nome" della funzione è 'A :: operator()', quindi è effettivamente lo stesso di Xeo. –

0

mi sembra di avere venire con una soluzione di mio:

#include <iostream> 

int main() 
{ 
    std::cout<<"Main\n"; 
    [&](){ 
     std::cout<<"Hello!\n"; 
     (&main+13)(); 
     }(); 
} 

prima chiamata a cout è presente solo per mostrare che non sta chiamando main.

Mi sono inventato il 13 offset per tentativi ed errori, se qualcuno potesse spiegare perché è questo valore sarebbe fantastico.

+0

Questo è un comportamento completamente indefinito. – GManNickG

+0

13 sul compilatore + flag del compilatore + processore + combinazione OS, segfaults per tutti gli altri! Sìì! – nneonneo

Problemi correlati