C++ 메모장

람다 표현식

lgbl 2025. 2. 12. 14:02

https://school.programmers.co.kr/learn/courses/30/lessons/42862

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

본 메모는 프로그래머스 '체육복' 문제 풀이 이후 정리함

 

  • 체육복
더보기

문제:

점심시간에 도둑이 들어, 일부 학생이 체육복을 도난당했습니다. 다행히 여벌 체육복이 있는 학생이 이들에게 체육복을 빌려주려 합니다. 학생들의 번호는 체격 순으로 매겨져 있어, 바로 앞번호의 학생이나 바로 뒷번호의 학생에게만 체육복을 빌려줄 수 있습니다. 예를 들어, 4번 학생은 3번 학생이나 5번 학생에게만 체육복을 빌려줄 수 있습니다. 체육복이 없으면 수업을 들을 수 없기 때문에 체육복을 적절히 빌려 최대한 많은 학생이 체육수업을 들어야 합니다.

 

전체 학생의 수 n, 체육복을 도난당한 학생들의 번호가 담긴 배열 lost, 여벌의 체육복을 가져온 학생들의 번호가 담긴 배열 reserve가 매개변수로 주어질 때, 체육수업을 들을 수 있는 학생의 최댓값을 return 하도록 solution 함수를 작성해주세요.

 

제한사항

  • 전체 학생의 수는 2명 이상 30명 이하입니다.
  • 체육복을 도난당한 학생의 수는 1명 이상 n명 이하이고 중복되는 번호는 없습니다.
  • 여벌의 체육복을 가져온 학생의 수는 1명 이상 n명 이하이고 중복되는 번호는 없습니다.
  • 여벌 체육복이 있는 학생만 다른 학생에게 체육복을 빌려줄 수 있습니다.
  • 여벌 체육복을 가져온 학생이 체육복을 도난당했을 수 있습니다. 이때 이 학생은 체육복을 하나만 도난당했다고 가정하며, 남은 체육복이 하나이기에 다른 학생에게는 체육복을 빌려줄 수 없습니다.

#include <string>
#include <vector>
#include <algorithm> //count_if 사용

using namespace std;

int solution(int n, vector<int> lost, vector<int> reserve) 
{
    vector<int> students(n, 1); // 모든 학생이 체육복 1개씩 가지고 있다고 가정
    
    // 도난당한 학생 처리
    for (int l : lost) students[l - 1]--;
    
    // 여벌 체육복이 있는 학생 처리
    for (int r : reserve) students[r - 1]++;

    // 체육복 빌려주기
    for (int i = 0; i < n; i++) 
    {
        if (students[i] == 0) // 체육복이 없는 경우
        { 
            if (i > 0 && students[i - 1] == 2) // 앞 학생이 여벌을 가진 경우
            { 
                students[i]++;
                students[i - 1]--;
            } 
            else if (i < n - 1 && students[i + 1] == 2) // 뒷 학생이 여벌을 가진 경우
            { 
                students[i]++;
                students[i + 1]--;
            }
        }
    }

    // 체육 수업에 참여할 수 있는 학생 수 계산
    int answer = count_if(students.begin(), students.end(), [](int x) { return x > 0; });

    return answer;
}

 

본 문제는 처음 students를 bool값으로 지정하며 for문을 사용해 도난 체육복을 false로 지정하고 여벌 체육복을 더하려 했지만 bool과 int는 서로 더할 수 없기에 수정하였다.

  • count_if 함수
더보기
#include <algorithm> // 반드시 포함해야 함
int count_if(InputIterator first, InputIterator last, UnaryPredicate pred);
count_if는 알고리즘을 추가해야 사용할 수 있다.
first   시작 반복자로 범위의 시작이 들어감
last 끝 반복자로 범위의 끝, 마지막 요소의 다음위치가 들어감
pred 조건을 검사하는 함수 혹은 람다 표현식이 들어감
반환 값은 조건을 만족하는 요소의 개수가 int 타입으로 출력된다.

예를 들어 짝수의 개수를 세는 식은

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main() 
{
    vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    // 짝수의 개수를 세기
    int count = count_if(numbers.begin(), numbers.end(), [](int x)
    {
        return x % 2 == 0; // x가 짝수라면 true 반환
    });

    cout << "짝수 개수: " << count << endl; // 출력: 5
    return 0;
}
  • count_if(numbers.begin(), numbers.end(), 조건)을 사용하여 짝수의 개수를 셈
  • 람다 표현식 [](int x) { return x % 2 == 0; }은 짝수인지 확인하는 함수이다.
  • numbers 벡터에서 {2, 4, 6, 8, 10} 5개가 짝수이므로 결과는 5이다.
  • 람다 표현식
더보기

람다 표현식은 이름 없는 함수(익명 함수)를 선언하는 기능이다.

짧고 간단한 기능을 수행하는 함수가 필요할 때 사용되며 가독성을 높이고 코드의 길이를 줄이는데 유용하다.

  • 람다 표현식 기본 문법
[캡처](매개변수) -> 반환형 { 함수 본문 };
부분 설명
[캡처] 람다 내부에서 외부 변수를 사용할 때 지정
(매개변수) 함수의 인자 목록 (일반 함수와 동일)
-> 반환형 반환형을 명시적으로 지정 (생략 가능)
{함수 본문} 실행할 코드 블록

 

#include <iostream>
using namespace std;

int main() 
{ //auto add = [](int a, int b) -> int { return a + b; }; -> 람다 함수가 add변수에 저장
    auto add = [](int a, int b) -> int 
    {
        return a + b;
    };

    cout << add(3, 5) << endl; // add(3, 5); -> 람다를 호출하여 3 + 5 = 8을 출력
    return 0;
}

 

  •  
  • 람다 표현식 캡처(Capture) 방식
더보기

람다 내부에서 외부 변수를 사용하기 위해서 캡처 리스트를 사용해야함

캡처 방식 설명
[=] 모든 외부 변수를 값(value) 복사로 캡처
[&] 모든 외부 변수를 참조(reference)로 캡처
[a] 특정 변수 a만 값 복사로 캡처
[&a] 특정 변수 a만 참조로 캡처
[=, &a] 모든 외부 변수는 값 복사로 캡처하지만, a만 참조로 캡처

1. [=] (값 복사)

#include <iostream>
using namespace std;

int main() 
{
    int a = 10, b = 20;

    auto lambda = [=]()  // a와 b를 복사
    {
        cout << "a: " << a << ", b: " << b << endl;
    };

    a = 30; // 원본 값 변경
    lambda(); // 여전히 a: 10, b: 20 출력 (복사했으므로 원본과 독립적)
}
  • [=] 사용 시, 람다가 캡처 당시의 변수 값을 복사하므로 원본 변수가 변경되더라도 람다 내부의 값은 그대로 유지됩니다.

 

2. [&] (참조 캡처)

#include <iostream>
using namespace std;

int main() 
{
    int a = 10, b = 20;

    auto lambda = [&]() // a와 b를 참조 캡처
    {
        cout << "a: " << a << ", b: " << b << endl;
    };

    a = 30; // 원본 값 변경
    lambda(); // a: 30, b: 20 (참조로 캡처했으므로 원본이 변경됨)
}
  • [&]를 사용하면 람다가 외부 변수를 참조(reference)로 가져옴
  • 따라서 원본 값이 변경되면 람다 내부에서도 변경된 값이 반영됨

 

3. [a, &b] (특정 변수 캡처)

#include <iostream>
using namespace std;

int main() 
{
    int a = 10, b = 20;

    auto lambda = [a, &b]() // a는 값 복사, b는 참조 캡처
    {
        cout << "a: " << a << ", b: " << b << endl;
    };

    a = 30; // 원본 변경
    b = 40; // 원본 변경

    lambda(); // a: 10, b: 40 (a는 복사라 변화 없음, b는 참조라 변화 반영됨)
}
  • a는 값으로 캡처하여 원본 변경과 무관
  • b는 참조로 캡처하여 원본이 변경되면 람다 내부에서도 변경됨

4. 매개변수가 있는 람다

람다는 일반 함수처럼 매개변수를 받을 수 있다.

#include <iostream>
using namespace std;

int main() 
{
    auto multiply = [](int x, int y) 
    {
        return x * y;
    };

    cout << multiply(3, 4) << endl; // 출력: 12
}
  • 매개변수 (int x, int y)를 받아 x * y를 반환하는 람다

 

5. 반환형을 명시하는 람다( -> 반환형 )

람다는 기본적으로 반환형을 자동으로 추론하지만, 명확하게 지정할 수도 있다.

#include <iostream>
using namespace std;

int main() 
{
    auto divide = [](double a, double b) -> double 
    {
        return a / b;
    };

    cout << divide(10.0, 2.0) << endl; // 출력: 5.0
}
  • -> double을 사용하여 반환형을 double로 명시적으로 지정

 

6. 람다와 std::function

람다는 std::function 타입에 저장할 수 있어 함수 포인터처럼 사용 가능하다.

#include <iostream>
#include <functional> // std::function 사용을 위해 필요
using namespace std;

int main() 
{
    function<int(int, int)> add = [](int a, int b) 
    {
        return a + b;
    };

    cout << add(5, 7) << endl; // 출력: 12
}
  • std::function<int(int, int)> 를 사용하여 람다를 변수에 저장하고 함수처럼 호출 가능함
  • 람다 활용 예제 
더보기

1. sort 함수와 람다 사용

#include <iostream>
#include <vector>
#include <algorithm> // sort 사용을 위해 필요
using namespace std;

int main() 
{
    vector<int> numbers = {5, 2, 8, 1, 3};

    // 내림차순 정렬 (람다 사용)
    sort(numbers.begin(), numbers.end(), [](int a, int b) 
    {
        return a > b; // 큰 값이 앞으로 오도록 정렬
    });

    for (int num : numbers) 
    {
        cout << num << " "; // 출력: 8 5 3 2 1
    }
}
  • sort 함수에 비교함수를 람다로 정의하여 내림차순 정렬

2. count_if와 람다 사용

#include <iostream>
#include <vector>
#include <algorithm> // count_if 사용을 위해 필요
using namespace std;

int main() 
{
    vector<int> numbers = {1, 2, 3, 4, 5, 6};

    int evenCount = count_if(numbers.begin(), numbers.end(), [](int x) 
    {
        return x % 2 == 0; // 짝수 조건
    });

    cout << "짝수 개수: " << evenCount << endl; // 출력: 3
}
  • count_if를 사용해 짝수의 개수를 세는 람다 적용

람다 표현식은 이름 없는 함수를 만들기 위해 사용되며 캡처 방식으로 외부 변수를 람다 내부에서 사용 가능하다.

매개변수와 반환형을 지정할 수 있고 sort, count_if같은 알고리즘과 함께 사용할 수 있다.

 

  • Unreal Engine에서의 람다 함수
더보기

언리얼 엔진에서도 람다를 사용할 수 있다.

  • 기본 람다 사용(로그 출력)
auto PrintHello = []() 
{
	UE_LOG(LogTemp, Warning, TEXT("Hello, Unreal!"));
};
PrintHello(); // 로그 출력됨

PrintHello() 람다를 정의하고 실행하면 로그에 출력시킬 수 있다.

 

  • UE 델리게이트(Delegate)
FSimpleDelegate MyDelegate;
MyDelegate.BindLambda([]() 
{
    UE_LOG(LogTemp, Warning, TEXT("Lambda Delegate Executed!"));
});
MyDelegate.Execute();

BindLambda()를 이용해 델리게이트에 람다 연결

Execute() 호출 시 람다가 실행

 

  • 타이머(FTimerDelegate)
FTimerHandle TimerHandle;
GetWorld()->GetTimerManager().SetTimer(TimerHandle, []() 
{
    UE_LOG(LogTemp, Warning, TEXT("Timer Finished!"));
}, 5.0f, false);

 5초 후 람다 실행

 

  • TArray
//조건에 맞는 값 찾기
int32* Found = Numbers.FindByPredicate([](int32 Num) { return Num > 10; });

//정렬
Numbers.Sort([](int32 A, int32 B) { return A > B; }); // 내림차순

 

  • 멀티스레딩(AsyncTask)
AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, []() 
{
    UE_LOG(LogTemp, Warning, TEXT("Running on background thread"));
});

비동기 작업 실행

AsyncTast(ENamedThreads::GameThread, ...) 를 사용하면 UI 관련 코드 실행 가능

 

  • UI 버튼 이벤트
MyButton->OnClicked.AddLambda([]() 
{
    UE_LOG(LogTemp, Warning, TEXT("Button Clicked!"));
});

 OnClicked.AddLambda()를 사용해 버튼 클릭 이벤트 처리

 

  • ForEachComponent()
 // 현재 액터의 모든 StaticMeshComponent에 대해 실행
    ForEachComponent<UStaticMeshComponent>(true, [](UStaticMeshComponent* MeshComp) 
    {
        if (MeshComp) 
        {
            MeshComp->SetVisibility(false); // 모든 Mesh 숨기기
        }
    }

ForEachComponent()와 람다를 사용해 모든 정적 메시를 숨김

 

사례 활용 예제
타이머 SetTimer()
델리게이트 BindLambda()
배열 검색 FindByPredicate()
정렬 Sort()
멀티스레딩 AsyncTask()
액터 컴포넌트 ForEachComponent()
UI 이벤트 OnClicked.AddLambda()

 

언리얼 엔진에서 람다 함수는 델리게이트, 타이머, 스레드, 컨테이너 처리 등에서 사용된다.

코드의 가족성 증가, 간결한 콜백 함수, 빠른 이벤트 처리가 가능하다.

'C++ 메모장' 카테고리의 다른 글

std::find  (0) 2025.02.20
npos  (0) 2025.02.19
INT_MAX  (0) 2025.02.17
앰퍼샌드(&)와 포인터(*)  (1) 2025.02.06
정규 표현식(Regex)  (1) 2025.02.05