람다 표현식과 함수형 프로그래밍
2025. 3. 28. 10:09ㆍProgramming Languages/C++
9.5 람다 표현식과 함수형 프로그래밍
9.5.1 람다 표현식 고급 기능
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <chrono>
#include <numeric>
class Timer {
private:
std::chrono::high_resolution_clock::time_point start;
std::string name;
public:
Timer(const std::string& n) : name(n) {
start = std::chrono::high_resolution_clock::now();
}
~Timer() {
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << name << " 실행 시간: " << duration.count() << " 마이크로초" << std::endl;
}
};
int main() {
// 기본 람다 표현식
auto add = [](int a, int b) {
return a + b;
};
std::cout << "3 + 4 = " << add(3, 4) << std::endl;
// 변수 캡처 (Capturing Variables)
int multiplier = 5;
// 값으로 캡처
auto multiplyByValue = [multiplier](int value) {
return value * multiplier;
};
// 참조로 캡처
auto multiplyByReference = [&multiplier](int value) {
multiplier++; // 외부 변수 수정 가능
return value * multiplier;
};
std::cout << "5 * 3 = " << multiplyByValue(3) << std::endl;
std::cout << "6 * 3 = " << multiplyByReference(3) << std::endl; // multiplier는 이제 6
std::cout << "multiplier = " << multiplier << std::endl; // 7 출력
// 모든 변수 캡처
auto captureAll = [=](int value) { // 모든 변수를 값으로 캡처
return value * multiplier;
};
auto captureAllRef = [&](int value) { // 모든 변수를 참조로 캡처
multiplier++;
return value * multiplier;
};
// 혼합 캡처
auto mixedCapture = [=, &multiplier](int value) { // multiplier만 참조로, 나머지는 값으로
multiplier++;
return value * multiplier;
};
// 초기 캡처 (C++14)
auto counter = [count = 0]() mutable {
return ++count;
};
std::cout << "카운터: " << counter() << std::endl; // 1
std::cout << "카운터: " << counter() << std::endl; // 2
// 제네릭 람다 (C++14)
auto genericAdd = [](auto a, auto b) {
return a + b;
};
std::cout << "정수 합: " << genericAdd(5, 3) << std::endl;
std::cout << "실수 합: " << genericAdd(3.14, 2.71) << std::endl;
std::cout << "문자열 합: " << genericAdd(std::string("Hello, "), "World!") << std::endl;
// 성능 비교 - 람다 vs 함수 객체 vs 일반 함수
const int ITERATIONS = 10000000;
std::vector<int> numbers(ITERATIONS);
std::iota(numbers.begin(), numbers.end(), 0); // 0부터 시작하는 연속 값 채우기
// 일반 함수
auto isEvenFunc = [](int n) { return n % 2 == 0; };
{
Timer t("람다 표현식");
int count = std::count_if(numbers.begin(), numbers.end(), isEvenFunc);
std::cout << "짝수 개수: " << count << std::endl;
}
// 함수 객체
struct IsEven {
bool operator()(int n) const {
return n % 2 == 0;
}
};
{
Timer t("함수 객체");
int count = std::count_if(numbers.begin(), numbers.end(), IsEven());
std::cout << "짝수 개수: " << count << std::endl;
}
// 함수 포인터
bool isEven(int n) {
return n % 2 == 0;
}
{
Timer t("함수 포인터");
int count = std::count_if(numbers.begin(), numbers.end(), isEven);
std::cout << "짝수 개수: " << count << std::endl;
}
// 람다 표현식을 std::function에 저장
std::function<int(int, int)> operation;
char op;
std::cout << "연산자 입력 (+, -, *, /): ";
std::cin >> op;
switch (op) {
case '+':
operation = [](int a, int b) { return a + b; };
break;
case '-':
operation = [](int a, int b) { return a - b; };
break;
case '*':
operation = [](int a, int b) { return a * b; };
break;
case '/':
operation = [](int a, int b) { return b != 0 ? a / b : 0; };
break;
default:
operation = [](int a, int b) { return a + b; };
}
std::cout << "10 " << op << " 5 = " << operation(10, 5) << std::endl;
return 0;
}
9.5.2 함수형 프로그래밍 패턴
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <numeric>
#include <string>
// 함수형 프로그래밍 유틸리티
namespace fp {
// 컨테이너의 모든 요소에 함수 적용 (map)
template<typename Container, typename Function>
auto map(const Container& container, Function func) {
using ResultType = decltype(func(*container.begin()));
std::vector<ResultType> result;
result.reserve(container.size());
std::transform(container.begin(), container.end(),
std::back_inserter(result), func);
return result;
}
// 조건을 만족하는 요소만 필터링 (filter)
template<typename Container, typename Predicate>
Container filter(const Container& container, Predicate pred) {
Container result;
std::copy_if(container.begin(), container.end(),
std::back_inserter(result), pred);
return result;
}
// 컨테이너 요소를 단일 값으로 축소 (reduce)
template<typename Container, typename Function, typename T>
T reduce(const Container& container, Function func, T initialValue) {
return std::accumulate(container.begin(), container.end(),
initialValue, func);
}
// 두 함수를 합성하여 새 함수 생성 (compose)
template<typename F, typename G>
auto compose(F f, G g) {
return [=](auto x) {
return f(g(x));
};
}
// 부분 적용 함수 (curry)
template<typename Function, typename FirstArg>
auto curry(Function func, FirstArg first) {
return [=](auto... args) {
return func(first, args...);
};
}
}
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 1. 맵(Map) - 각 요소 변환
auto squared = fp::map(numbers, [](int x) { return x * x; });
std::cout << "제곱 결과: ";
for (int num : squared) {
std::cout << num << " ";
}
std::cout << std::endl;
// 2. 필터(Filter) - 조건에 맞는 요소 선택
auto evens = fp::filter(numbers, [](int x) { return x % 2 == 0; });
std::cout << "짝수만: ";
for (int num : evens) {
std::cout << num << " ";
}
std::cout << std::endl;
// 3. 리듀스(Reduce) - 단일 값으로 축소
int sum = fp::reduce(numbers, [](int acc, int x) { return acc + x; }, 0);
std::cout << "합계: " << sum << std::endl;
// 맵-필터-리듀스 조합 (짝수만 제곱한 후 합계 계산)
int result = fp::reduce(
fp::map(
fp::filter(numbers, [](int x) { return x % 2 == 0; }),
[](int x) { return x * x; }
),
[](int acc, int x) { return acc + x; },
0
);
std::cout << "짝수 제곱의 합: " << result << std::endl;
// 4. 함수 합성(Composition)
auto double_it = [](int x) { return x * 2; };
auto increment = [](int x) { return x + 1; };
// double_it을 적용한 후 increment 적용
auto double_then_increment = fp::compose(increment, double_it);
// increment를 적용한 후 double_it 적용
auto increment_then_double = fp::compose(double_it, increment);
std::cout << "double_then_increment(5): " << double_then_increment(5) << std::endl; // 11
std::cout << "increment_then_double(5): " << increment_then_double(5) << std::endl; // 12
// 5. 커리(Curry)와 부분 적용
auto add = [](int a, int b) { return a + b; };
auto add5 = fp::curry(add, 5); // 첫 번째 인자를 5로 고정
std::cout << "add5(10): " << add5(10) << std::endl; // 15
// 커리된 함수로 맵 수행
auto add5ToAll = fp::map(numbers, add5);
std::cout << "모든 요소에 5 더하기: ";
for (int num : add5ToAll) {
std::cout << num << " ";
}
std::cout << std::endl;
// 함수형 스타일로 데이터 처리 예제
std::vector<std::string> names = {"Alice", "Bob", "Charlie", "Dave", "Eve", "Frank"};
// 이름이 5글자 이상인 사람만 선택하여 대문자로 변환 후 이름 길이의 합계 계산
int totalLength = fp::reduce(
fp::map(
fp::filter(names, [](const std::string& name) { return name.length() >= 5; }),
[](const std::string& name) {
std::string upper = name;
std::transform(upper.begin(), upper.end(), upper.begin(), ::toupper);
return upper;
}
),
[](int acc, const std::string& name) { return acc + name.length(); },
0
);
std::cout << "5글자 이상 이름의 총 길이: " << totalLength << std::endl;
return 0;
}
9.6 정규 표현식(Regex) (C++11)
#include
#include
#include
#include
void printMatches(const std::string& text, const std::regex& regex) {
std::smatch matches;
std::string searchText = text;
std::cout << "「" << text << "」에서 찾은 결과:" << std::endl;
while (std::regex_search(searchText, matches, regex)) {
std::cout << " - " << matches[0] << std::endl;
searchText = matches.suffix();
}
std::cout << std::endl;
}
int main() {
// 기본 패턴 매칭
std::string text = "The quick brown fox jumps over the lazy dog";
std::regex wordRegex("\\b\\w{5}\\b"); // 정확히 5글자인 단어 찾기
printMatches(text, wordRegex);
// 이메일 주소 검증
std::vector emails = {
"user@example.com",
"invalid.email",
"another.user@gmail.com",
"not_an_email",
"name.surname@company.co.uk"
};
std::regex emailRegex("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$");
std::cout << "이메일 주소 유효성 검사:" << std::endl;
for (const auto& email : emails) {
bool isValid = std::regex_match(email, emailRegex);
std::cout << email << ": " << (isValid ? "유효" : "유효하지 않음") << std::endl;
}
std::cout << std::endl;
// 전화번호 형식 변환
std::string phoneNumbers = "연락처: 010-1234-5678, 02-987-6543, 01087654321";
std::regex phoneRegex("(\\d{2,3})[-]?(\\d{3,4})[-]?(\\d{4})");
// 형식 변환 (일관된 형식으로)
std::string formattedNumbers = std::regex_replace(
phoneNumbers,
phoneRegex,
"$1-$2-$3"
);
std::cout << "원본 전화번호: " << phoneNumbers << std::endl;
std::cout << "형식 변환 후: " << formattedNumbers << std::endl << std::endl;
// 캡처 그룹 사용
std::string htmlText = "
안녕하세요
이것은 HTML 텍스트입니다.
";
std::regex tagRegex("<([a-z]+)>(.*?)");
std::cout << "HTML 태그 분석:" << std::endl;
std::sregex_iterator it(htmlText.begin(), htmlText.end(), tagRegex);
std::sregex_iterator end;
while (it != end) {
std::smatch match = *it;
std::cout << "태그: " << match[1] << ", 내용: " << match[2] << std::endl;
++it;
}
std::cout << std::endl;
// HTML 태그 제거
std::string plainText = std::regex_replace(htmlText, std::regex("<[^>]*>"), "");
std::cout << "태그 제거 후 텍스트: " << plainText << std::endl << std::endl;
// URL 파싱
std::string url = "https://www.example.com:8080/path/to/resource?param1=value1¶m2=value2";
std::regex urlRegex("(https?)://([^:/]+)(?::(\\d+))?(/[^?#]*)?(?:\\?([^#]*))?");
std::smatch urlMatch;
if (std::regex_match(url, urlMatch, urlRegex)) {
std::cout << "URL 분석 결과:" << std::endl;
std::cout << " 프로토콜: " << urlMatch[1] << std::endl;
std::cout << " 호스트: " << urlMatch[2] << std::endl;
std::cout << " 포트: " << (urlMatch[3].matched ? urlMatch[3] : "기본값") << std::endl;
std::cout << " 경로: " << (urlMatch[4].matched ? urlMatch[4] : "/") << std::endl;
std::cout << " 쿼리: " << (urlMatch[5].matched ? urlMatch[5] : "없음") << std::endl;
}
return 0;
}
'Programming Languages > C++' 카테고리의 다른 글
다중 스레딩과 동시성(C++11) (0) | 2025.03.28 |
---|---|
스마트 포인터(Smart Pointers) (C++11) (0) | 2025.03.28 |
파일 입출력 (0) | 2025.03.28 |
고급 주제 (0) | 2025.03.28 |
챕터8. 실습 문제 (0) | 2025.03.28 |