11.8 Introduction to lambdas (anonymous functions)

주홍영·2022년 3월 14일
0

Learncpp.com

목록 보기
122/199

https://www.learncpp.com/cpp-tutorial/introduction-to-lambdas-anonymous-functions/

Lambdas

Lambda expression 은 우리에게 anonymous function을 다른 function 안에서 정의할 수 있도록 도와준다. naming pollution을 피할 수 있고 함수를 사용하는 곳에서 가깝게 정의할 수 있다는 장점이 있다

람다식의 형태는 다음과 같다

[ captureClause ] ( parameters ) -> returnType
{
    statements;
}

capture clause와 parameter는 모두 empty일 수 있다, 만약 필요치 않다면
(capture clause가 무엇인지는 다음 섹션에서 설명한다)
return type은 optional이다. 만약 명시하지 않는다면 auto로 간주된다

람다식을 사용해 다음과 같이 코드를 작성할 수 있다

#include <algorithm>
#include <array>
#include <iostream>
#include <string_view>

int main()
{
  constexpr std::array<std::string_view, 4> arr{ "apple", "banana", "walnut", "lemon" };

  // Define the function right where we use it.
  const auto found{ std::find_if(arr.begin(), arr.end(),
                           [](std::string_view str) // here's our lambda, no capture clause
                           {
                             return (str.find("nut") != std::string_view::npos);
                           }) };

  if (found == arr.end())
  {
    std::cout << "No nuts\n";
  }
  else
  {
    std::cout << "Found " << *found << '\n';
  }

  return 0;
}

Type of a lambda

우리는 앞서 살펴몬 example에서 람다식이 변수가 필요한 바로 그 지점에 직접 정의되어 사용한 것을 보았다. 이러한 람다의 쓰임을 function literal 이라고 한다

하지만 lambda식을 하나의 line에 모두 쓰면 가독성이 매우 떨어진다
line 분리를 한다고해도 가독성이 떨어지는 것은 마찬가지다

따라서 우리는 lambda variable을 initialize 해서 사용하는 방법에 대해서 배울 것이다
다음의 예시를 살펴보자

// Bad: We have to read the lambda to understand what's happening.
return std::all_of(array.begin(), array.end(), [](int i){ return ((i % 2) == 0); });

우리는 위의 코드를 아래와 같이 개선할 수 있다

// Good: Instead, we can store the lambda in a named variable and pass it to the function.
auto isEven{
  [](int i)
  {
    return ((i % 2) == 0);
  }
};

return std::all_of(array.begin(), array.end(), isEven);

위에서 auto를 이용해 isEven이라는 lambda variable을 instantiate해서 사용하고 있다
그렇다면 isEven의 정확한 type은 무엇일까?

람다는 명시적으로 사용할 수 있는 타입이 존재하지 않는다
우리가 만약 lambda를 사용하면 컴파일러가 자체적으로 특별한 타입을 만들어 사용한다
이 타입은 우리에게 노출되지 않는다

For advanced readers

사실 람다는 함수가 아니다 람다는 functor의 한 종류이다
functor는 operator() overloading한 오브젝트이다

람다를 post def로 사용하는 방법에는 여러가지 방법이 있다

하나는 function pointer (capture clause가 empty인 경우)
또 하나는 std::function을 통해서 사용할 수 있다 (이는 capture clause가 empty가 아니어도 가능)

예시는 다음과 같다

#include <functional>

int main()
{
  // A regular function pointer. Only works with an empty capture clause.
  double (*addNumbers1)(double, double){
    [](double a, double b) {
      return (a + b);
    }
  };

  addNumbers1(1, 2);

  // Using std::function. The lambda could have a non-empty capture clause (Next lesson).
  std::function addNumbers2{ // note: pre-C++17, use std::function<double(double, double)> instead
    [](double a, double b) {
      return (a + b);
    }
  };

  addNumbers2(3, 4);

  // Using auto. Stores the lambda with its real type.
  auto addNumbers3{
    [](double a, double b) {
      return (a + b);
    }
  };

  addNumbers3(5, 6);

  return 0;
}

그 외 자세한 설명은 링크 참조

Generic lambdas

대부분의 경우 람다 매개변수는 일반 함수 매개변수와 동일한 규칙에 따라 작동합니다.

흠 generic lambda에 대해서는 생략한다

Standard library function objects

일반적인 연산(예: 덧셈, 부정 또는 비교)의 경우 표준 라이브러리에 대신 사용할 수 있는 많은 기본 호출 가능 객체가 포함되어 있기 때문에 고유한 람다를 작성할 필요가 없습니다.
이들은 < functional > 헤더에 정의되어 있습니다.

#include <algorithm>
#include <array>
#include <iostream>
#include <functional> // for std::greater

int main()
{
  std::array arr{ 13, 90, 99, 5, 40, 80 };

  // Pass std::greater to std::sort
  std::sort(arr.begin(), arr.end(), std::greater{}); // note: need curly braces to instantiate object

  for (int i : arr)
  {
    std::cout << i << ' ';
  }

  std::cout << '\n';

  return 0;
}

std::greater{}가 library에 정의된 객체이다

Conclusion

람다와 알고리즘 라이브러리는 루프를 사용하는 솔루션과 비교할 때 불필요하게 복잡해 보일 수 있습니다. 그러나 이 조합을 사용하면 몇 줄의 코드로 매우 강력한 작업을 수행할 수 있으며 자체 루프를 작성하는 것보다 더 읽기 쉽습니다. 게다가 알고리즘 라이브러리는 루프에서는 얻을 수 없는 강력하고 사용하기 쉬운 병렬 처리를 제공합니다. 라이브러리 함수를 사용하는 소스 코드를 업그레이드하는 것이 루프를 사용하는 코드를 업그레이드하는 것보다 쉽습니다.
Lambdas are great, but they don’t replace regular functions for all cases. Prefer regular functions for non-trivial and reusable cases.

profile
청룡동거주민

0개의 댓글