[C++] std::for_each(), for_each()

728x90
반응형

for_each()

for_each란?

algorithm 헤더에 정의된 STL함수로 주어진 범위 내의 모든 요소를 순회하면 요소 값을 함수에 전달하는 기능을 수행한다.

std::for_each(시작 iterator, 끝 iterator, 함수 또는 람다 표현식)

vector의 요소를 출력하는 예제

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

void print(int value)
{
    std::cout << value << ", ";
}

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

    std::for_each(numbers.begin(), numbers.end(), print);

    return 0;
}

실행 결과는 다음과 같다.

1, 2, 3, 4, 5

[[vector]]로 선언된 numbers의 시작(begin())과 끝(end())의 [[iterator]]를 for_each 함수의 매개변수에 전달 인자로 전달하고, numbers의 요소를 전달하면서 실행할 함수를 마지막 매개변수 위치에 넣어준다.

for_each()의 전달 인자가 되는 함수에 매개변수가 존재하지 않는 경우?

[[#vector의 요소를 출력하는 예제]]에서 void print(int value) 함수에 매개변수 int valeu가 없다면 컴파일러는 다음과 같은 에러를 발생 시킨다.

void print()
{
    std::cout << "print()";
}

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

    std::for_each(numbers.begin(), numbers.end(), print);

    return 0;
}

위와 같이 코드를 작성할 경우 void print() 함수는 매개변수가 없기 때문에 전달받은 vector요소를 처리할 수 없어 에러가 발생하게 된다.

for_each컴파일 에러

for_each에서 호출하는 함수의 반환형

for_each에 매개변수로 전달해 실행하는 함수의 반환형은 무시된다. 아래 코드는 컴파일 에러도 발생하지 않고 [[#vector의 요소를 출력하는 예제]]에서 제시한 코드의 실행결과와 동일한 실행결과를 보인다.

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

int print(int value)
{
    std::cout << value << ", ";

    return 1;
}

int main()
{
    std::vector<int> numbers = {1, 2, 3, 4, 5};
    std::for_each(numbers.begin(), numbers.end(), print);
    return 0;
}

for_each()의 전달 인자가 되는 함수의 매개 변수가 두 개 이상인 경우?

[[#for_each()의 전달 인자가 되는 함수에 매개변수가 존재하지 않는 경우?]]와 마찬가지로 컴파일 에러가 발생한다. for_each()에 전달 인자로 전달되는 함수의 전달 인자는 반드시 1개여야 한다.

for_each()에 [[lambda(람다)]]적용

vector의 요소를 출력하는 예제(lambda)

[[#vector의 요소를 출력하는 예제]]에서 void print(int value)를 람다식으로 대체해보자.

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

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

    std::for_each(numbers.begin(), numbers.end(), [](int value)) {
        std::cout<< value << ", ";    
    };

    return 0;
}

람다를 사용하면 코드를 더 짧고 명확하게 작성 할 수 있다.

for_each()를 왜 사용해야 하는가?

for_each()for 반복문 비교

for_each()와 전통적인 for 반복문의 성능 차이는 거의 없다고 봐도 무방하다. for_each()가 더 효율적이고 간결하다고 말하는 이유는 코드의 가독성표현력에 대한 차이점 때문이다.
for_each()를 사용하는 이유 중 하나는 코드의 간결성함수형 프로그래밍스타일을 강조 할 수 있다는 점이 있다.

std::for_each(numbers.begin(), numbers.end(), print)

이 코드는 컨테이너의 모든 요소를 pinrt함수의 매개 변수에 전달 인자로 전달한다는 것을 명확하게 보여준다. 또한 컨테이너와 알고리즘(여기서는 print함수)을 분리해서 코드의 의도를 더 명확하게 표현할 수 있다.

반면에 for문을 사용하면

for(int i = 0; i < 5; i++){
    std:;cout << numbers[i] << ", ";
}

이 방식도 for_each()를 사용한 경우와 같은 결과를 보이지만 iterator의 범위를 직접 제어하고 인덱스를 사용해야 하므로 코드가 조금 더 절차적이고 덜 추상화된 형태가 된다. 따라서 코드 길이가 길어지거나 복잡해질 경우 개발자의 실수를 유발할 가능성이 있다.

알고리즘과 데이터를 분리

for를 사용하게 되면 알고리즘과 데이터가 엉켜서 명령형 코딩 스타일을 따르게 된다. for_each()를 사용하게 되면 함수형 스타일로 처리를 할 수 있게 한다.

아래 코드와 같이 for문을 사용하면 각 요소에 대해 무엇을 할 것인가?(알고리즘)컨테이너에서 데이터를 어떻게 가져올 것인가?(데이터 처리) 를 혼합해서 작성해야 한다.

for(int i = 0; i < numbers.size(); ++i) { // 데이터를 어떻게 가져올 것인가 ?
    numbers[i] *= 2; // 알고리즘
}

하지만 for_each()를 사용하면 데이터 처리는 컨테이너에 위임하고, 알고리즘 x2만을 집중해서 처리할 수 있다.

std::for_each(numbers.begin(), numbers.end(), [](double& num)) {
    num *= 2;
}

이렇게 람다식을 적용하면 데이터와 알고리즘의 책임이 분리되어 코드의 유지보수성이 높아진다.

동일한 코드 패턴의 재사용성

반복 작업에 대해 동일한 패턴을 재사용 할 수 있다. 예를 들어 여러 vectorlist를 처리할 때, 코드를 반복해서 작성하지 않고 동일한 패턴으로 쉽게 적용 할 수 있다.

void print(double i)
{
    std::cout << i << ", ";
}

std::vector<double> nums1 = {1.0, 2.0, 3.0};
std::vector<double> nums2 = {11.0, 12.0, 13.0};

std::for_each(nums1.begin(), nums1.end(), print);
std::for_each(nums2.begin(), nums2.end(), print);

이와 같이 작성하면 코드 중복을 줄이고 같은 패턴의 작업을 효율적으로 처리할 수 있다. 필요에 따라 람다식이나 함수 객체로 바꿔 줄 수 있다.

모든 iterator 사용 가능

계속해서 예를 들고 있는 vector뿐만 아니라 [[list]], [[set]], [[map]] 등 다양한 컨테이너에서 사용 가능하다.

직관적인 의미 전달

코드의 의미를 직관적으로 전달할 수 있는 장점이 있다. for문은 순회를 직접 제어하는 명령형 프로그래밍 스타일로 코드가 복잡해질 수 있지만, for_each각 요소에 대해 어떤 작업을 할지 라는 개념을 쉽게 표현할 수 있다.

std::for_each(numbers.begin(), numbers.end(), print);

이 코드는 vector의 각 요소를 print함수로 출력한다. 라는 적을 직관적으로 이해 할 수 있다.

-끝-

728x90
반응형