sort를 사용하기 전에 람다식을 알게되면 sort를 편하게 사용할 수 있는 경우가 많다.
C++에서 람다식(lambda expression)은 익명 함수(anonymous function)를 정의하는 방법으로, 짧고 간결하게 함수 객체를 생성할 수 있습니다. 람다식은 일반적으로 코드 블록을 함수 인라인으로 정의할 때 사용됩니다. C++11부터 도입되었으며, 이후의 C++ 표준에서도 지속적으로 개선되고 있습니다.
람다식의 기본 구조는 다음과 같습니다:
[capture](parameters) -> return_type
{
// 함수 본문
}
C++
복사
구성 요소
1.
캡처 리스트 (Capture list): 람다식이 정의된 범위 내의 변수를 캡처하여 람다식 내부에서 사용할 수 있게 합니다. [] 안에 캡처할 변수들을 명시합니다.
•
[=] : 외부 변수들을 값으로 캡처 (복사).
•
[&] : 외부 변수들을 참조로 캡처.
•
[x, &y] : x는 값으로, y는 참조로 캡처.
2.
매개변수 리스트 (Parameters list): 함수와 마찬가지로 람다식이 받을 매개변수를 정의합니다.
3.
반환 타입 (Return type): 반환 타입을 명시적으로 지정할 수 있으며, > 기호를 사용합니다. C++14부터는 반환 타입을 생략할 수 있습니다. 컴파일러가 자동으로 추론합니다.
4.
함수 본문 (Function body): 람다식의 실제 코드가 들어가는 부분입니다.
예제
1.
간단한 람다식 예제:
auto add = [](int a, int b) -> int {
return a + b;
};
int result = add(3, 4); // result는 7
C++
복사
1.
외부 변수 캡처:
int x = 10;
int y = 20;
auto addXY = [x, &y](int a) {
return a + x + y;
};
int result = addXY(5); // result는 35, x는 10, y는 20
C++
복사
1.
캡처 리스트 예제:
int a = 1, b = 1;
auto fib = [=]() mutable {
int temp = a;
a = b;
b = temp + b;
return temp;
};
for (int i = 0; i < 5; ++i) {
std::cout << fib() << " "; // 1 1 2 3 5
}
C++
복사
1.
람다식을 인자로 전달:
#include <algorithm>
#include <vector>
#include <iostream>
int main()
{
std::vector<int> vec = { 1, 2, 3, 4, 5 };
std::for_each(vec.begin(), vec.end(),
[](int& n)
{
n *= 2;
});
for (int n : vec)
{
std::cout << n << " "; // 2 4 6 8 10
}
return 0;
}
C++
복사
람다식은 함수 객체와 비교해 간결하고 가독성이 좋으며, 코드 작성 및 유지보수를 용이하게 해줍니다. C++11부터 시작하여 C++14, C++17, C++20 표준까지 지속적으로 기능이 확장되어 더욱 유용하게 사용될 수 있습니다.
algorithm 헤더를 사용해서 sort 정렬 함수를 사용 할 수 있다.
#include <algorithm>
C++
복사
std::sort 함수 원형
template<class RandomIt>
void sort(RandomIt first, RandomIt last);
template<class RandomIt, class Compare>
void sort(RandomIt first, RandomIt last, Compare comp);
C++
복사
•
first: 정렬을 시작할 범위의 첫 번째 요소를 가리키는 반복자
•
last: 정렬을 종료할 범위의 마지막 다음 요소를 가리키는 반복자
•
comp: (optional) 정렬 기준을 제공하는 함수나 함수 객체로 람다식으로 표현될 수 있다
만약 비교함수 comp가 사용자에 의해 제공되지 않은 경우 기본적으로 < 연산자를 사용하여 비교를 수행하며, 이를 통해 오름차순으로 정렬한다.
비교함수 멤버함수 사용
#include <bits/stdc++.h>
#define fastio cin.tie(0)->sync_with_stdio(0)
using namespace std;
// 구조체 내부에 비교 연산자를 정의
struct Info {
int val;
// operator < 를 오버로딩하여 정렬 기준을 정의
// sort 함수는 기본적으로 < 연산자를 사용하므로
// 이 연산자를 재정의하면 정렬 순서를 변경할 수 있음
bool operator < (const Info& i) const {
return val > i.val; // val이 큰 원소가 앞에 오도록 정렬 (내림차순)
}
};
int main() {
fastio;
vector<Info> v;
// 1부터 5까지의 값을 가진 Info 객체를 벡터에 추가
for (int i = 1; i <= 5; i++) v.push_back(Info{ i });
// sort 함수 호출 시 Info 구조체의 operator < 가 자동으로 사용됨
sort(v.begin(), v.end());
// 결과 출력: 5 4 3 2 1 (내림차순)
for (auto& i : v) cout << i.val << ' ';
cout << '\n';
}
C++
복사
비교함수 전역함수 사용
#include <bits/stdc++.h>
#define fastio cin.tie(0)->sync_with_stdio(0)
using namespace std;
// 전역 비교 함수 정의
// 문자열의 길이를 우선 비교하고, 길이가 같으면 사전 역순으로 정렬
bool Cmp(const string& a, const string& b)
{
// 1순위: 문자열 길이 비교 (짧은 것이 앞으로)
if (a.size() != b.size()) return a.size() < b.size();
// 2순위: 길이가 같은 경우 사전 역순 정렬 (z에 가까운 것이 앞으로)
return a > b;
}
int main()
{
fastio;
vector<string> v{ "stack", "queue", "list", "set", "map" };
// sort 함수의 세 번째 인자로 비교 함수를 전달
sort(v.begin(), v.end(), Cmp);
// 결과: set map(길이 3) -> list(길이 4) -> stack queue(길이 5, 사전 역순)
for (auto& i : v) cout << i << ' ';
cout << '\n';
}
C++
복사
비교함수 작성(람다식 사용)
auto compare = [](int a, int b) { return a < b; }
C++
복사
두 요소를 받아들여 비교한 결과를 반환하여 정렬의 순서를 결정하는 비교함수를 작성한다.
비교 함수의 동작 원리:
•
비교 함수가 true를 반환하면: 첫 번째 인자(a)가 두 번째 인자(b)보다 앞에 와야 함을 의미
•
비교 함수가 false를 반환하면: 두 인자의 순서를 바꾸지 않거나 b가 a보다 앞에 옴
•
return a < b;의 의미: "a가 b보다 작으면 a를 앞에 배치" → 결과적으로 오름차순 정렬
•
return a > b;의 의미: "a가 b보다 크면 a를 앞에 배치" → 결과적으로 내림차순 정렬
위와 같이 람다식을 사용해 인수로 넘겨줄 비교함수를 작성할 수 있다.
보통 정수형 벡터의 경우 오름차순으로 정렬할 때는 return a < b; 내림차순으로 정렬할 때는 return a > b;로 정의하는데 여기서 의문이 생긴다.
"a < b가 참일 때 두 요소의 순서가 바뀐다면 큰 수가 앞에 오게되니까 내림차순 정렬되는게 아닌가?"
그런데 실제로는 그렇지 않다. 이는 비교 함수의 인자 전달 순서 때문이다.
핵심 이해:
•
sort 알고리즘은 배열을 순회하면서 인접한 요소들을 비교한다
•
비교 함수 compare(a, b)가 호출될 때:
◦
a는 현재 비교 대상이 되는 뒤쪽 요소
◦
b는 이미 정렬된 영역의 요소
•
compare(a, b)가 true를 반환하면 "a가 b보다 앞에 와야 한다"는 의미
•
따라서 a < b가 참이면 "작은 값(a)을 앞으로" → 오름차순
아래 예제에서 실제 비교 과정을 출력해보면 이 사실을 명확히 알 수 있다.
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
vector<int> numbers = {5, 4, 3, 2, 1};
// 비교 함수 내부에서 비교 과정을 출력
sort(numbers.begin(), numbers.end(),
[](int a, int b)
{
// a와 b가 어떤 순서로 비교되는지 출력
printf("비교: a=%d, b=%d -> ", a, b);
bool result = a < b;
printf("결과=%s\n", result ? "true(a를 앞에)" : "false(순서 유지)");
return result;
});
cout << "\nSorted numbers in ascending order: ";
for (const auto& num : numbers)
{
cout << num << " ";
}
cout << endl;
// 실행 결과를 보면:
// - a에는 현재 삽입하려는 요소가 들어옴
// - b에는 이미 정렬된 부분의 요소가 들어옴
// - a < b가 true면 a를 b보다 앞에 배치 → 작은 수가 앞으로 → 오름차순
return 0;
}
C++
복사
정수형 벡터 오름차순 정렬 예시
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
// 정렬할 정수형 벡터 초기화
vector<int> numbers = {5, 2, 8, 1, 7, 3};
// sort 함수를 사용한 오름차순 정렬
// 비교 함수를 생략하면 기본적으로 < 연산자를 사용 (오름차순)
sort(numbers.begin(), numbers.end());
cout << "Sorted numbers in ascending order: ";
for (const auto& num : numbers)
{
cout << num << " ";
}
cout << endl;
return 0;
}
C++
복사
람다식으로 비교함수를 정의하여 정수 내림차순 정렬 예시
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
vector<int> numbers = {5, 2, 8, 1, 7, 3};
// 람다식으로 내림차순 비교 함수 정의
// a > b가 true면 "큰 값(a)을 앞에 배치" → 내림차순
sort(numbers.begin(), numbers.end(),
[](int a, int b)
{
return a > b;
});
cout << "Sorted numbers in descending order: ";
for (const auto& num : numbers) {
cout << num << " ";
}
cout << endl;
// 출력 결과: 8 7 5 3 2 1
return 0;
}
C++
복사
람다식 비교함수에서 외부 변수를 캡쳐하여 구조체를 정렬하는 예시
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 사람 정보를 담는 구조체
struct Person
{
string name;
int age;
};
int main()
{
vector<Person> people =
{
{"Alice", 25},
{"Bob", 30},
{"Charlie", 22},
{"David", 28}
};
// 나이를 기준으로 오름차순 정렬
// const Person&로 참조를 받아 불필요한 복사를 방지
// -> bool은 반환 타입을 명시 (생략 가능)
sort(people.begin(), people.end(),
[](const Person& a, const Person& b) -> bool
{
return a.age < b.age; // 나이가 적은 사람이 앞에 오도록
});
cout << "Sorted people by age:" << endl;
for (const auto& person : people)
{
cout << person.name << " (" << person.age << ")" << endl;
}
// 출력:
// Charlie (22)
// Alice (25)
// David (28)
// Bob (30)
return 0;
}
C++
복사
원본 배열을 직접 변경하지 않고 원본 배열의 요소들이 정렬될 순서를 담은 인덱스 배열을 확보하는 예시
이 기법은 다음과 같은 상황에서 유용합니다:
•
여러 기준으로 정렬을 수행하되 원본 데이터를 보존해야 할 때
•
정렬 순서를 추적하여 다른 배열에도 같은 순서를 적용해야 할 때
•
원본 배열의 인덱스 정보가 중요한 경우
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric> // iota 함수를 위해 필요
using namespace std;
int main()
{
// 원본 배열: 인덱스 0=5, 1=2, 2=8, 3=1, 4=7, 5=3
vector<int> numbers = {5, 2, 8, 1, 7, 3};
// 인덱스를 저장할 벡터 (원본 배열과 같은 크기)
vector<int> indices(numbers.size());
// iota: 0부터 시작하여 1씩 증가하는 값으로 초기화
// indices = {0, 1, 2, 3, 4, 5}
iota(indices.begin(), indices.end(), 0);
// 핵심: 인덱스 배열을 정렬하되, 비교는 원본 배열의 값을 기준으로
// [&numbers]: 람다식에서 numbers 배열을 참조로 캡처
sort(indices.begin(), indices.end(),
[&numbers](int i1, int i2)
{
// i1, i2는 인덱스 값
// numbers[i1], numbers[i2]는 해당 인덱스의 실제 값
return numbers[i1] < numbers[i2]; // 원본 배열 값으로 비교
});
// 정렬된 인덱스 순서 출력
cout << "Sorted indices based on the values: ";
for (int i : indices)
{
cout << i << " "; // 예상 출력: 3 1 5 0 4 2
}
cout << endl;
// 해석: numbers[3]=1(가장 작음) -> numbers[1]=2 -> ... -> numbers[2]=8(가장 큼)
// 인덱스를 사용하여 정렬된 순서로 원본 값 출력
cout << "Original array in sorted order: ";
for (int i : indices)
{
cout << numbers[i] << " "; // 출력: 1 2 3 5 7 8
}
cout << endl;
// 원본 배열은 그대로 유지됨
cout << "Original array unchanged: ";
for (int n : numbers)
{
cout << n << " "; // 출력: 5 2 8 1 7 3
}
cout << endl;
return 0;
}
C++
복사
그 외적으로 여러가지 stl에서 제공되는 알고리즘 함수들
다양한 STL 알고리즘 함수
예약어 | 설명 |
for_each | 모든 요소를 지정한 함수로 조작 |
find | 요소 검색 |
find_if | 지정한 조건을 만족하는 요소 검색 |
min_element | 최소 요소 리턴 |
max_element | 최대 요소 리턴 |
sort | 요소 정렬 |
count | 지정한 숫자와 일치하는 요소 수 리턴 |
count_if | 지정한 조건을 만족하는 요소 수 리턴 |
all_of | 모든 요소가 지정한 조건을 만족하면 true 리턴 |
none_of | 모든 요소가 지정한 조건을 만족하지 않으면 true 리턴 |
any_of | 모든 요소 중에 어느 하나라도 지정한 조건을 만족하면 true 리턴 |
fill | 모든 요소에 지정한 값을 대입 |
copy | 모든 요소를 복사 |
copy_if | 지정한 조건을 만족하는 요소만 복사 |
generate | 모든 요소에 지정한 연산 결과를 대입 |
transform | 모든 요소를 지정한 함수로 변환 |
remove | 지정한 숫자에 일치하는 요소를 제거 |
remove_if | 지정한 조건을 만족하는 요소를 제거 |
replace | 지정한 숫자를 지정한 숫자로 변경 |
replace_if | 지정한 조건에 만족하는 요소를 지정한 숫자로 변경 |
random_shuffle | 모든 요소를 셔플 |
accumulate | 모든 요소의 집계 계산 |
“강의는 많은데, 왜 나는 아직도 코드를 못 짤까?”
혼자 공부하다 보면 누구나 이런 고민을 하게 됩니다.
•
강의는 다 들었지만 막상 손이 안 움직이고,
•
복습을 하려 해도 무엇을 다시 봐야 할지 모르겠고,
•
질문할 곳도 없고,
•
유튜브는 결국 정답을 따라 치는 것밖에 안 되는 것 같고.
문제는 ‘연습’이 빠졌기 때문입니다.
단순히 강의를 듣는 것만으로는 실력이 늘지 않습니다.
실제 문제를 풀고, 고민하고, 직접 구현해보는 시간이 반드시 필요합니다.
그래서, 얌얌코딩 코칭은 다릅니다.
그냥 가르치지 않습니다.
스스로 설계하고, 코딩할 수 있게 만듭니다.
얌얌코딩 코칭에서는 단순한 예제가 아닌,
스스로 문제를 분석하고 구현해야 하는 연습문제를 제공합니다.
이 연습문제들은 다음과 같은 역량을 키우기 위해 설계되어 있습니다:
•
문제를 스스로 쪼개고 설계하는 힘
•
다양한 조건을 만족시키는 실제 구현 능력
•
기능 단위가 아닌, 프로그램 단위로 사고하는 습관
•
마침내 자신의 힘으로 코드를 끝까지 작성하는 경험
지금 필요한 건 더 많은 강의가 아닙니다.
코드를 스스로 완성해 나가는 훈련,
그것이 지금 실력을 끌어올릴 가장 현실적인 방법입니다.

