code

C ++ 14의 Lambda-Over-Lambda

codestyles 2020. 11. 5. 08:02
반응형

C ++ 14의 Lambda-Over-Lambda


다음 재귀 람다 호출은 어떻게 종료 / 종료됩니까?

#include <cstdio>

auto terminal = [](auto term)            // <---------+  
{                                        //           |
    return [=] (auto func)               //           |  ???
    {                                    //           |
        return terminal(func(term));     // >---------+
    };
};


auto main() -> int
{
    auto hello =[](auto s){ fprintf(s,"Hello\n"); return s; };
    auto world =[](auto s){ fprintf(s,"World\n"); return s; };


    terminal(stdout)
            (hello)
            (world) ;

    return 0;

}

여기서 내가 뭘 놓치고 있니?

Running code


재귀 함수 호출이 아닙니다. 단계별로 살펴보십시오.

  1. terminal(stdout) -이것은 단순히 캡처 한 람다를 반환합니다. stdout
  2. 1의 결과는 lambda와 함께 호출되어 hello람다 ( func(term)) 를 실행하고 그 결과는로 전달 terminal()되며 1과 같이 단순히 람다를 반환합니다.
  3. 2의 결과는 람다로 호출 world되는데, 이는 2와 동일하게 수행되며 이번에는 반환 값이 삭제됩니다.

호출 자체는 재귀 적이 지 않습니다. 호출되면 terminal또 다른 함수 객체를 생성하기 위해 다시 호출 할 함수 객체를 반환 합니다.

따라서 다른 함수 객체 terminal(stdout)를 캡처 stdout하고 호출 할 수 있는 펑터를 반환 합니다. 다시 (hello)호출 hello하면 캡처 된 용어를 사용 하여 펑터를 호출하여 stdout출력합니다 "Hello". 호출 terminal및 반환이 시간의 반환 값 캡처 다른 펑 hello- 아직도있다 stdout. 그 펑터를, (world)다시 똑같이 호출 하여 "World".


여기서 핵심은 이것이 유효하다는 것을 이해하는 것입니다.

world(hello(stdout));

"Hello World"를 인쇄합니다. 재귀 적 일련의 람다는 다음과 같이 풀 수 있습니다.

#include <cstdio>

auto terminal = [](auto term)            // <---------+  
{                                        //           |
    return [=] (auto func)               //           |  ???
    {                                    //           |
        return terminal(func(term));     // >---------+
    };
};

/*
terminal(stdout) -returns> anonymous_lambda which captures stdout (functor)
anonymous_lambda(hello) is called, func(term) is hello(stdout) and prints "Hello" and returns stdout, the anonymous_lambda -returns> terminal(stdout)
(the above 2 lines start again)
terminal(stdout) is called and -returns> anonymous_lambda which captures stdout (functor)
anonymous_lambda(world) is called, func(term) is world(stdout) and prints "World" and returns stdout, the anonymous_lambda -returns> terminal(stdout)
terminal(stdout) is called and -returns> anonymous_lambda which captures stdout (functor)
nobody uses that anonymous_lambda.. end.
*/

auto main() -> int
{
    auto hello =[](auto s){ fprintf(s,"Hello\n"); return s; };
    auto world =[](auto s){ fprintf(s,"World\n"); return s; };

    world(hello(stdout));


    terminal(stdout)
            (hello)
            (world) ;

    return 0;

}

Coliru 예


내부적으로 다음과 같은 것으로 번역 될 수 있습니다.

#include <cstdio>

template <typename T>
struct unnamed_lambda
{
    unnamed_lambda(T term) : captured_term(term) {}

    template <typename A>
    unnamed_lambda operator()(A func);

    T captured_term;
};

struct terminal_lambda
{
    template <typename A>
    unnamed_lambda<A> operator()(A term)
    {
        return unnamed_lambda<A>{term};
    }
};

terminal_lambda terminal;

template <typename T>
template <typename A>
unnamed_lambda<T> unnamed_lambda<T>::operator()(A func)
{
    return terminal(func(captured_term));
}

struct Hello
{
    FILE* operator()(FILE* s)
    {
        fprintf(s, "Hello\n");
        return s;
    }
};

struct World
{
    FILE* operator()(FILE* s)
    {
        fprintf(s, "World\n");
        return s;
    }
};

int main()
{    
    Hello hello;
    World world;
    unnamed_lambda<FILE*> l1 = terminal(stdout);
    unnamed_lambda<FILE*> l2 = l1(hello);
    unnamed_lambda<FILE*> l3 = l2(world);

    // same as:
    terminal(stdout)(hello)(world);
}

LIVE DEMO

실제로 이것은 컴파일러가 람다 (일부 근사값 포함)로 장면 뒤에서 수행하는 작업 입니다.


I think that the source of confusion comes from reading a lambda declaration as a lambda call. Indeed here:

auto terminal = [](auto term)            // <---------+  
{                                        //           |
    return [=] (auto func)               //           |  ???
    {                                    //           |
        return terminal(func(term));     // >---------+
    };
};

the author just declared a lambda terminal which takes one arbitrary argument term and returns an unnamed lambda, nothing more! Let's look at this unnamed lambda, it:

  • accepts a callable object func as argument and calls it on the copy-captured parameter term and
  • returns the result of terminal called with the result of the call func(term); so it returns another unnamed lambda that captures the result of func(term), it but this lambda is not called by now, there is no recursion.

Now the trick in the main should be more clear:

  1. terminal(stdout) returns an unnamed lambda which has captured stdout.
  2. (hello) calls this unnamed lambda passing as arg the hello callable. This gets called on the stdout previously captured. hello(stdout) returns again stdout which is used as argument of a call to terminal, returning another unnamed lambda which has captured stdout.
  3. (world) same as 2.

  1. terminal(stdout) returns a function, let's call it function x, with param func. So:

    terminal(stdout) ==> x(func) { return terminal(func(stdout)) };

  2. Now terminal(stdout)(hello) calls function x(hello):

    terminal(stdout)(hello) ==> x(hello) { return terminal(hello(stdout)) };

    This results in hello function get called and returns function x again.

  3. Now terminal(std)(hello)(world) calls function x(world):

    terminal(stdout)(hello) ==> x(world) { return terminal(world(stdout)) };

    This results in world function get called and returns function x again. Function x now is not called any more as there is no more param.

참고URL : https://stackoverflow.com/questions/25618934/lambda-over-lambda-in-c14

반응형