함수 객체 및 람다 표현식
Coddy C++ 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 104개 중 74번째.
많은 STL 알고리즘은 동작을 커스터마이징하는 호출 가능한 객체(callable object)를 받아들입니다. 여러분은 이미 std::transform과 함께 사용되는 람다를 보았습니다. C++에서 호출 가능한 객체를 만드는 두 가지 방법인 함수 객체(functors)와 람다 표현식(lambda expressions)에 대해 알아보겠습니다.
펑터(함수 객체)는 operator()를 오버로드하여 인스턴스를 함수처럼 호출할 수 있게 하는 클래스입니다:
#include <algorithm>
#include <vector>
#include <iostream>
struct MultiplyBy {
int factor;
MultiplyBy(int f) : factor(f) {}
int operator()(int x) const {
return x * factor;
}
};
int main() {
std::vector<int> nums = {1, 2, 3, 4};
std::vector<int> result(nums.size());
std::transform(nums.begin(), nums.end(), result.begin(), MultiplyBy(3));
// 결과: {3, 6, 9, 12}
}펑터는 일반 함수와 달리 호출 사이에 상태를 유지할 수 있습니다. 하지만 간단한 연산을 위해 클래스를 정의하는 것은 번거롭습니다. 람다 표현식은 간결한 대안을 제공합니다:
int factor = 3;
std::transform(nums.begin(), nums.end(), result.begin(),
[factor](int x) { return x * factor; });람다 구문은 [capture](parameters) { body }입니다. 캡처 절은 람다가 액세스할 수 있는 외부 변수를 지정합니다. 모든 변수를 값으로 캡처하려면 [=]를 사용하고, 모든 변수를 참조로 캡처하려면 [&]를 사용하며, 또는 [factor]나 [&factor]와 같이 특정 변수를 나열합니다.
람다는 사용자 정의 기준에 따른 정렬과 같은 알고리즘을 사용하는 일회성 작업에 특히 유용합니다:
std::vector<int> nums = {5, -2, 8, -1};
std::sort(nums.begin(), nums.end(),
[](int a, int b) { return std::abs(a) < std::abs(b); });
// 절댓값 기준으로 정렬됨: {-1, -2, 5, 8}챌린지
쉬움제품 가격에 다양한 할인 전략을 적용하기 위해 펑터(functor)와 람다 표현식을 모두 사용하는 가격 계산기를 만들어 보겠습니다.
코드는 두 개의 파일로 구성됩니다:
Discounts.h: 여기에 할인 펑터와 유틸리티 함수를 정의합니다.할인율(정수형)을 저장하는
PercentageDiscount라는 이름의 펑터를 만드세요. 이 펑터의operator()는double가격을 인자로 받아 할인된 가격을 반환해야 합니다. 예를 들어, $100에 대한 20% 할인은 $80를 반환해야 합니다.차감할 고정 금액(
double형)을 저장하는FixedDiscount라는 이름의 또 다른 펑터를 만드세요. 이 펑터의operator()는 가격을 인자로 받아 가격에서 고정 금액을 뺀 값을 반환해야 합니다 (단, 결과가 0보다 작아서는 안 됩니다).const std::vector<double>&를 인자로 받아 모든 가격을 공백으로 구분하여 출력하고 줄바꿈을 수행하는printPrices함수를 만드세요. 각 가격은 소수점 둘째 자리까지 표시해야 합니다.main.cpp: 다섯 개의 입력을 읽습니다 (각각 별도의 줄에 입력됨):- 첫 번째 제품 가격 (double)
- 두 번째 제품 가격 (double)
- 세 번째 제품 가격 (double)
- 적용할 할인율 (정수, 예: 20%의 경우 20)
- 고정 할인 금액 (double)
세 개의 가격으로 벡터를 생성하고 두 가지 방식을 모두 시연하세요:
Original prices:를 출력한 후 가격들을 출력합니다.std::transform과PercentageDiscount펑터를 사용하여 할인된 가격의 새 벡터를 생성합니다.After percentage discount:를 출력한 후 결과를 출력합니다.- 원본 가격에
std::transform과FixedDiscount펑터를 사용하여 또 다른 벡터를 생성합니다.After fixed discount:를 출력한 후 결과를 출력합니다. - 각 원본 가격을 두 배로 만드는 람다 표현식과
std::transform을 사용합니다.Premium prices (doubled):를 출력한 후 결과를 출력합니다. - 람다를 사용한
std::sort로 원본 가격을 내림차순으로 정렬합니다.Sorted (high to low):를 출력한 후 정렬된 가격을 출력합니다.
예를 들어, 입력값이 100.00, 50.00, 75.00, 20, 15.00인 경우:
Original prices: 100.00 50.00 75.00
After percentage discount: 80.00 40.00 60.00
After fixed discount: 85.00 35.00 60.00
Premium prices (doubled): 200.00 100.00 150.00
Sorted (high to low): 100.00 75.00 50.00 이 챌린지를 통해 할인 금액과 같은 상태를 유지하는 펑터와 빠른 인라인 작업을 위해 변수를 캡처하는 람다를 비교해 볼 수 있습니다. 두 방식 모두 std::transform 및 std::sort와 같은 STL 알고리즘과 원활하게 작동합니다.
치트 시트
STL 알고리즘은 동작을 커스터마이징하기 위해 호출 가능한 객체(callable objects)를 받아들입니다. 두 가지 일반적인 접근 방식은 함수 객체(functors)와 람다 식(lambda expressions)입니다.
함수 객체(functor)는 operator()를 오버로드하여 인스턴스를 함수처럼 호출할 수 있게 하는 클래스입니다:
struct MultiplyBy {
int factor;
MultiplyBy(int f) : factor(f) {}
int operator()(int x) const {
return x * factor;
}
};
std::transform(nums.begin(), nums.end(), result.begin(), MultiplyBy(3));함수 객체는 호출 간에 상태를 유지할 수 있지만, 장황한 클래스 정의가 필요합니다.
람다 식(Lambda expressions)은 [capture](parameters) { body } 구문을 사용하여 간결한 대안을 제공합니다:
int factor = 3;
std::transform(nums.begin(), nums.end(), result.begin(),
[factor](int x) { return x * factor; });캡처 절(capture clause)은 람다가 접근할 수 있는 외부 변수를 지정합니다:
[=]- 모든 변수를 값으로 캡처[&]- 모든 변수를 참조로 캡처[factor]- 특정 변수를 값으로 캡처[&factor]- 특정 변수를 참조로 캡처
람다는 커스텀 정렬 기준을 위해 std::sort와 같은 알고리즘과 함께 잘 작동합니다:
std::sort(nums.begin(), nums.end(),
[](int a, int b) { return std::abs(a) < std::abs(b); });직접 해보기
#include <iostream>
#include <vector>
#include <algorithm>
#include "Discounts.h"
int main() {
// 입력값 읽기
double price1, price2, price3;
int percentageDiscount;
double fixedDiscount;
std::cin >> price1;
std::cin >> price2;
std::cin >> price3;
std::cin >> percentageDiscount;
std::cin >> fixedDiscount;
// 세 개의 가격을 포함하는 vector 생성
std::vector<double> prices = {price1, price2, price3};
// TODO: "Original prices:"를 출력한 후 printPrices를 사용하여 가격들을 출력
// TODO: PercentageDiscount 펑터와 함께 std::transform 사용
// 결과를 저장할 새로운 vector 생성
// "After percentage discount:"를 출력한 후 결과들을 출력
// TODO: 원본 가격들에 대해 FixedDiscount 펑터와 함께 std::transform 사용
// 결과를 저장할 새로운 vector 생성
// "After fixed discount:"를 출력한 후 결과들을 출력
// TODO: 각 원본 가격을 두 배로 만드는 람다와 함께 std::transform 사용
// 결과를 저장할 새로운 vector 생성
// "Premium prices (doubled):"를 출력한 후 결과들을 출력
// TODO: 원본 가격을 내림차순으로 정렬하는 람다와 함께 std::sort 사용
// "Sorted (high to low):"를 출력한 후 정렬된 가격들을 출력
return 0;
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.