본문 바로가기
언리얼_엔진_게임개발_공부/C++

[C++] 람다 함수 lambda expression ~ accumulate() / transform() / partition() 등 STL 함수에 사용 예시

by jaboy 2025. 1. 8.

알고리즘 STL 의 함수 중 일부는 연산?을 매개 변수로 하여 인자를 전달 받아 실행된다.

e.g. sort(first, last, predicate) 에서 predicate 은 정렬의 기준이 되는 boolean 반환 타입의 함수이다.

이러한 STL 함수를 커스터마이즈 하기 위해 필요한 연산은 간단한 일회성인 경우가 많은데,

이 연산을 개별적인 함수로 정의하는 것 대신에 필요한 위치에서 '익명'의 함수 객체로 정의하여 사용하는 방식이 '람다' 함수 이다. 여러 가지 표현 방식이 있는데, 그 중 가장 간단한 방식으로 살펴보고자 한다.

(어차피 코드 가독성을 위해 간단한 연산 위주로 사용해야 한다.)

 

람다 표현식 / 람다식 / 람다 함수

[capture](parameters) -> return_type {
	// operation
}

- capture: 람다 표현식 외부의 변수를 값 또는 참조로 캡쳐하여 내부에서 사용 가능하다. 

- parameters: 연산에 필요한 매개 변수 선언

- return type: 반환할 자료형 - 보통 컴파일러가 추론하므로 생략 가능 (복잡한 컨디셔널 등이 있는 경우 명시해야 하지만, 그런 거라면 개별 함수로 정의하는 게 낫겠지?)

 

아래는 내림차순 정렬을 위해 sort() 함수에 람다 표현식을 인자로 전달하는 예시

sort(v.begin(), v.end(), [](int a, int b) {
	return a > b;
});

 

아래는 값 vs. 참조로 캡쳐 시 캡쳐된 변수의 값 차이

int x = 10;

auto addValue = [x]() {  // 값으로 캡처
    return x + 5;
};

auto addReference = [&x]() {  // 참조로 캡처
    return x + 5;
};

x = 20;  // x 값 변경

std::cout << "addValue: " << addValue() << std::endl;  // 15
std::cout << "addReference: " << addReference() << std::endl;  // 25

- 값으로 캡쳐 : 변수의 값이 addValue 할당 시점에 복사되어 정의된다.

- 참조로 캡쳐 : 변수가 참조이므로 addReference 호출 시점에 x 값이 사용된다.

 

std::accumulate()

https://en.cppreference.com/w/cpp/algorithm/accumulate

 

std::accumulate - cppreference.com

template< class InputIt, class T > T accumulate( InputIt first, InputIt last, T init ); (1) (constexpr since C++20) template< class InputIt, class T, class BinaryOp > T accumulate( InputIt first, InputIt last, T init, BinaryOp op ); (2) (constexpr since C+

en.cppreference.com

 

포맷

1)

template< class InputIt, class T >
T accumulate( InputIt first, InputIt last, T init );

 

[first, last) 범위의 원소들과 init 값의 합계 반환

 

2)

template< class InputIt, class T, class BinaryOp >
T accumulate( InputIt first, InputIt last, T init, BinaryOp op );

 

init 부터 시작해서 [first, last) 범위의 원소들에 대해 차례로 op 연산을 반복해 나간 결과 반환

 

2번 형식의 예를 위 람다 표현식을 활용해 아래와 같이 살펴볼 수 있다.

vector<int> v{1, 2, 3, 4, 5};

auto dash_fold = [](sstring a, int b) {
	return a + '-' + to_string(b);
}

string s = accumulate(++v.begin(), v.end(), to_string(v[0]), dash_fold);
cout << s;

// output : 1-2-3-4-5

v[0] 부터 시작해서 dash_fold 연산을 v 의 원소에 대해 순차적으로 해 나간 결과 값을 반환한 것이다.

 

std::transform()

https://en.cppreference.com/w/cpp/algorithm/transform

 

std::transform - cppreference.com

template< class InputIt, class OutputIt, class UnaryOp > OutputIt transform( InputIt first1, InputIt last1,                     OutputIt d_first, UnaryOp unary_op ); (1) (constexpr since C++20) template< class ExecutionPolicy,           clas

en.cppreference.com

 

transform(input_first, input_last, output_first, operation)

transform(input1_first, input1_last, input2_first, output_first, operation)

주어진 입력 범위에 대해 주어진 함수를 실행하고 그 결과를 출력 범위에 저장한다.

두 번째 형식의 경우 input1 과 input2 에 대해 함수를 실행한다. (input1 이 지정한 길이 기준)

 

아래는 정수 배열을 받아 각 정수의 제곱을 반환하도록 사용한 예시

vector<int> v = {1, 2, 3, 4, 5};
vector<int> v2(v.size());

transform(v.begin(), v.end(), v2.begin(), [](int x) {
	return x*x;
});

// v2 = {1, 4, 9, 16, 25}

이것 역시 람다 표현식 사용하여 transform 호출 시 함수를 인자로 넘겨줄 수 있다.

 

std::partition()

https://en.cppreference.com/w/cpp/algorithm/partition

 

std::partition - cppreference.com

template< class ForwardIt, class UnaryPred > ForwardIt partition( ForwardIt first, ForwardIt last, UnaryPred p ); (1) (constexpr since C++20) template< class ExecutionPolicy, class ForwardIt, class UnaryPred > ForwardIt partition( ExecutionPolicy&& policy,

en.cppreference.com

 

partition(first, last, predicate)

[first, last) 범위의 원소에 대해 predicate 이 반환하는 bool 값에 따라 true 인 원소들이 먼저 오도록 재정렬하고, false 그룹의 첫 원소를 가리키는 반복자를 반환한다.

*나뉠 때 순서가 보존되지 않는다.

 

vector<int> v = {1, 2, 3, 4, 5};

partition(v.begin(), v.end(), [](int x) {
	return x % 2 == 0;
});

짝수가 먼저 오고 이후에 홀수가 오도록 분할된다. 역시 predicate을 인자로 넘길 때 람다표현식을 이용할 수 있다.