Corgi Dog Bark

ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [C++] lambda 함수 기본부터
    카테고리 없음 2022. 11. 16. 14:25
    반응형

     

    In C++11 and later, a lambda expression—often called a lambda—is a convenient way of defining an anonymous function object (a closure) right at the location where it's invoked or passed as an argument to a function.
    MSDN-Lambda expressions in C++

    람다 함수는 기존 클로저(Closure)의 역할을 좀 더 쉽게 구현하게끔 하기 위하여 설계되었으며, C+11에서 처음 제시되었습니다. 여기서 클로저란 함수처럼 호출될 수 있는 이름 없는 객체를 일반적으로 일컫는 용어입니다.

     

    클로저의 간단한 예시를 들어보자면, 

    더보기
    struct call_voice
    {
    	void operator()() {
    		cout << "voice called" << endl;
    	}
    };
    
    int main() 
    {
        // 클로저 객체 생성
        call_voice call_my_voice();
        // 클로저 call
        call_my_voice();
    }

    와 같이 함수처럼 호출할 수 있는 객체를 의미합니다. C++ 에서는 이러한 객체를 구성하기 위해서는 반드시 operator 오버 로딩을 통한 ( ) 함수 호출 연산자를 구현해야만 합니다.

     

    따라서 람다 함수를 사용하게 된다면 객체를 선언해주고, operator 오버로딩을 할 필요 없이, 단순히 람다 함수만으로 closure 객체를 편리하게 만들 수 있다는 장점이 존재합니다. 따라서 요번 글에서는 어떻게 람다 함수를 구성하고 사용하는지 알아보겠습니다.

     

     

    1. Function 객체 그리고 Lambda


    람다함수를 생각하기 전에 함수(우리가 아는 함수) 그리고 Function Object(함수 객체: operator를 오버 로딩해서 사용하는 경우) 에는 각각 장단점이 존재했습니다. 먼저 전자인 함수의 경우, 간결하고 쓰기 편했지만 내부에 어떠한 상태를 유지하지는 못했습니다. 예를 들어 class와 같이 내부 변수를 가지고 있다든지 말입니다.

     

    그리고 Funciton Object 의 경우, 상태를 유지해서 내부 변수를 가지고 있을 수 있었지만, 간결하지 못하고 객체를 생성해줘야 하는 불편함이 존재했었습니다. 따라서 Lambda는 이러한 불편함을 해소하기 위해서 만들어졌습니다. 람다는 유연하고 내부 상태를 유지할 수 있게끔 설계되었으며, 간결하게 정의하고 사용할 수 있다는 장점이 존재합니다.

     

    더 나아가 기존 방법이 lambda 와 비교했을때, 어떠한 장점이 있는지 알아보도록 하겠습니다.!

     

     

    2. Lambda 사용법


    람다 함수를 위해서는 캡처목록 / 파라미터 / 본문 이렇게 3가지로 구성이 됩니다.

    람다 함수의 구성 요소

    원래는 6가지로 구성되어 있지만, 좀 더 간단하게, 3가지로 설명드리겠습니다.

    1. 캡처 목록: 위의 lambda-introducer에 나온 내용으로 내부 상태를 유지하는 변수로 선언됩니다. 바깥의 객체를 call-by-value로 가져올 수 도 있으며, Reference 형태로도 가져올 수 있습니다. 또한 내부 변수를 따로 선언하는 것도 가능합니다. 이때 value 로 복사하게 되었다면, 자동적으로 const 속성이 부여되어, mutable 키워드를 뒤에 설명하는 파라미터 뒤에 붙여줘야 합니다.
    2. 파라미터: lambda-parameter-declaration 은 함수와 같이 어떠한 변수를 받아 실행할 것인지를 의미합니다.
    3. 함수 본문: compound-statement를 의미하며, 함수 내부에서 어떠한 로직을 실행하고, return 할 것인지를 정의합니다.

     

    그러면 STL을 사용하여, 간단한 예시를 들어보겠습니다. vector를 생성한 다음에, 벡터에 1~10까지의 수를 넣은

    1. 다음 짝수면 "is_even" 그리고, 홀수면 "is_odd" 를 출력하도록 하고,
    2. 짝수의 개수를 세어주도록 하는 함수를 만들어보겠습니다.
    3. for_each 문을 써서 vector 를 순회하도록 하겠습니다.

    우선 vector 는 {1,2,3,4,5,6,7,8,9,10}과 같이 초기화가 되어있으며, 어떻게 설계해야 할지 고민해보겠습니다. 우선적으로 함수를 사용한다면, 다음과 같이 설계할 수 있을 것입니다.

     

     

    2-1. 함수를 사용한 경우

    제일 먼저 함수를 사용하게 된다면 다음과 같이 사용할 수 있습니다.

    void call_function(int& v) {
    	if (v % 2 == 0) {
    		cout << "is even" << endl;
    	}
    	else {
    		cout << "is odd" << endl;
    	}
    }
    
    vector<int> vec{ 1,2,3,4,5,6,7,8,9,10 };
    
    for_each(begin(vec), end(vec), call_function);

    이 경우, 1번 조건은 만족시키지만, 2번 조건 즉, 내부 상태를 유지하지 못하기 때문에, 짝수를 return 할 수 없습니다. 그렇다면 함수 객체(Function Object) 객체를 생성하는 것을 고려해보도록 하겠습니다.

     

     

    2-2. 함수 객체 사용

    struct call_function_object {
    	call_function_object(int& v) :inner_value(v) {}
    	int &inner_value;
    
    	void operator()(int& v) {
    		if (v % 2 == 0) {
    			cout << "is even" << endl;
    			inner_value++;
    		}
    		else {
    			cout << "is odd" << endl;
    		}
    	}
    };
    
    vector<int> vec{ 1,2,3,4,5,6,7,8,9,10 };
    int count_odd = 0;
    for_each(begin(vec), end(vec), call_function_object(count_odd));
    cout << count_odd << endl;

     함수 객체를 사용해서 다음과 같이 조금은 길지만, 1,2,3 번을 만족시키는 함수를 작성할 수 있었습니다. 그러면 그다음으로 람다 함수를 사용하여 생성하여 간단히 구현하는 예제를 살펴보도록 하겠습니다.

     

     

    2-3. 람다함수 사용

    우선 식을 보여드리기 전에 람다 함수는 객체를 캡처하고 내부에서 사용할 수 있도록 하기 때문에, &count 변수를 통해서 내부에서 컨트롤할 수 있도록 하겠습니다.

    vector<int> vec{ 1,2,3,4,5,6,7,8,9,10 };
    int count_even = 0;
    for_each(begin(vec), end(vec), [&count_even](int n) {
        if (n % 2 == 0) {
            cout << "is even" << endl;
            count_even++;
        }
        else {
            cout << "is odd" << endl;
        }
    });
    cout << count_even << endl;

    다음과 같이 람다 함수를 사용하여, 조금은 깔끔한 코드 작성을 할 수 있게 되었습니다.

     

     

    람다 Lambada 정리


    사실 람다에는 this 포인터라든지 mutable 그리고, constexpr 을 통한 메타 프로그래밍도 가능하게 하는 강력한 도구들이 들어가 있습니다. 하지만 오늘 살펴본 내용은 람다의 기본적인 내용들로서, 이것만으로도 충분히 강력한 역할을 한다고 자신있게 말씀드리고 싶습니다..!

     

    긴 글 읽어주셔서 감사합니다.

    도움이 되셨거나, 잘못된 내용이 있다면 댓글 부탁드리겠습니다.

    반응형

    댓글

Designed by Tistory.