람다 익스프레션과 STL 하이어 오더 펑션
이번 시간에는 람다 익스프레션과 STL에서 제공하는 하이어 오더 펑션(Higher-Order Function) 이 함께 사용되는 예제를 살펴봅니다.
이번 내용은 STL 알고리즘의 시간 복잡도나 내부 동작보다는, 람다 익스프레션이 STL 함수와 어떻게 결합되어 사용되는지를 중심으로 설명합니다. STL 알고리즘 자체에 대한 자세한 내용은 별도의 STL 알고리즘 챕터에서 다룹니다.
1. 하이어 오더 펑션(Higher-Order Function)이란?
하이어 오더 펑션은 컴퓨터 사이언스에서 다음과 같이 정의됩니다.
함수를 인자로 받거나, 함수를 반환하는 함수
즉, 일반적인 데이터만 전달받는 함수가 아니라 다른 함수를 매개변수로 사용할 수 있는 함수를 의미합니다.
예를 들어 이전 시간에 살펴본 std::for_each가 대표적인 하이어 오더 펑션입니다.
std
::for_each(
begin,
end,
function
);
여기서 세 번째 인자로 함수 객체 또는 람다 익스프레션을 전달하고 있습니다.
따라서 std::for_each는 함수를 인자로 받는 하이어 오더 펑션입니다.
2. remove_if와 람다 익스프레션
이번에는 std::remove_if를 이용해 특정 조건의 데이터를 제거하는 예제를 살펴보겠습니다.
예를 들어 벡터 안에서 홀수를 제거하고 싶다고 가정하겠습니다.
먼저 홀수를 판단하는 람다 함수를 작성합니다.
auto filterOdd = [](int n)
{
return n % 2 == 1;
};
이 함수는:
- 홀수 → true
- 짝수 → false
를 반환합니다.
그리고 remove_if에 전달합니다.
std::remove_if(
nums.begin(),
nums.end(),
filterOdd
);
실행하면 기존:
1 2 3 4 5 6 7 8 9 10
에서 홀수가 필터링되는 결과를 확인할 수 있습니다.
람다를 직접 전달하는 방식
함수 객체를 따로 만들지 않고 바로 작성할 수도 있습니다.
std::remove_if(
nums.begin(),
nums.end(),
[](int n)
{
return n % 2 == 1;
}
);
두 방식은 동일하게 동작합니다.
현대적인 C++ 코드에서는 간단한 경우 람다를 직접 전달하는 스타일도 많이 사용됩니다.
3. sort와 람다 익스프레션
STL의 sort 역시 하이어 오더 펑션처럼 비교 함수를 전달받습니다.
기본 정렬:
std::sort(nums.begin(), nums.end());
은 오름차순 정렬입니다.
하지만 직접 비교 조건을 만들 수도 있습니다.
std::sort(
nums.begin(),
nums.end(),
[](int a, int b)
{
return a < b;
}
);
여기서:
a < b
라는 조건을 통해 정렬 기준을 정의합니다.
반대로:
return a > b;
로 변경하면:
10 9 8 7 ... 1
과 같은 내림차순 정렬이 됩니다.
4. 사용자 정의 정렬 기준
람다를 사용하면 단순한 크기 비교뿐만 아니라 원하는 조건으로 정렬할 수 있습니다.
예를 들어 숫자 5와 가까운 순서로 정렬한다고 가정합니다.
std::sort(
nums.begin(),
nums.end(),
[](int a, int b)
{
return std::abs(a - 5) < std::abs(b - 5);
}
);
그러면:
- 5와 가까운 숫자가 앞쪽
- 5와 먼 숫자가 뒤쪽
으로 정렬됩니다.
이처럼 람다를 이용하면 개발자가 원하는 다양한 정렬 기준을 쉽게 만들 수 있습니다.
5. reduce와 람다 익스프레션
STL에는 여러 값을 하나의 값으로 합치는 reduce 함수도 있습니다.
대표적인 예는 전체 합 계산입니다.
int sum = std::reduce(
nums.begin(),
nums.end(),
0,
[](int a, int b)
{
return a + b;
}
);
동작 방식:
- 초기값: 0
- 두 값을 받아 더함
- 결과를 계속 누적
따라서:
1 + 2 + 3 + ... + 10
의 결과를 얻을 수 있습니다.
곱셈 계산
초기값과 연산만 변경하면 곱셈도 가능합니다.
int result = std::reduce(
nums.begin(),
nums.end(),
1,
[](int a, int b)
{
return a * b;
}
);
결과:
1 × 2 × 3 × ... × 10
을 계산합니다.
6. reduce와 accumulate 차이
비슷한 기능을 하는 함수로 std::accumulate가 있습니다.
둘은 큰 차이는 없지만:
- accumulate
- 기존부터 존재
- 순차 계산
- reduce
- C++17부터 추가
- 병렬 계산 정책 지원 가능
이라는 차이가 있습니다.
reduce는 실행 정책을 통해 병렬 처리를 지원하기 때문에 대규모 데이터 처리에서 더 유리할 수 있습니다.
7. 현대 C++에서 람다와 STL 조합
현대적인 C++에서는 직접 반복문을 작성하기보다:
- remove_if
- sort
- for_each
- transform
- reduce
같은 STL 함수와 람다 익스프레션을 조합하는 방식이 많이 사용됩니다.
예를 들어 객체를 관리하는 경우에도:
- 나이가 홀수인 객체 제거
- 특정 기준으로 정렬
- 특정 값 변환
- 데이터 합산
등을 쉽게 작성할 수 있습니다.
예:
cats.erase(
std::remove_if(
cats.begin(),
cats.end(),
[](Cat& cat)
{
return cat.GetAge() % 2 == 1;
}
),
cats.end()
);
처럼 객체 기반 코드에서도 활용할 수 있습니다.
핵심 정리
하이어 오더 펑션
- 함수를 인자로 받거나 반환하는 함수
- STL 알고리즘에서 많이 사용
대표 예:
- for_each
- remove_if
- sort
- reduce
람다 익스프레션과 STL의 장점
람다를 STL 함수에 전달하면:
- 별도의 함수 객체 작성 불필요
- 코드가 짧아짐
- 원하는 조건을 바로 표현 가능
- 가독성이 좋아짐
현대 C++에서는 람다 익스프레션과 STL 하이어 오더 펑션을 함께 사용하는 스타일이 매우 일반적입니다. 처음에는 문법이 낯설 수 있지만 익숙해지면 반복문과 조건문을 직접 작성하는 것보다 더 간결하고 표현력 높은 코드를 작성할 수 있습니다.
'Fundamental of CS > : : C++' 카테고리의 다른 글
| [Vector / Array] C++ Array (0) | 2024.07.31 |
|---|---|
| [Functional Programming] Function Pointer, std::function (0) | 2024.07.31 |
| [Functional Programming] Lambda This (0) | 2024.07.31 |
| [Functional Programming] Lambda Expression (0) | 2024.07.31 |
| [Functional Programming] Functional Programming, Function Object Intro (0) | 2024.07.31 |