[C++] transform 사용법

transform 기본 사용법

transform은 지정된 함수 객체를 입력된 범위의 모든 원소들에게 적용하고, 그 결과를 대상 범위에 출력하는 함수입니다.

 

이 함수를 사용하려면 먼저 다음 헤더를 포함해야 합니다.

#include <algorithm>

 

이 함수의 정의는 다음과 같습니다.

template<class InputIterator, class OutputIterator, class UnaryFunction>
OutputIterator transform(
    InputIterator first1,
    InputIterator last1,
    OutputIterator result,
    UnaryFunction func );

여기서, func는 입력된 범위의 원소를 매개 변수로 받는 함수 객체입니다.

( UnaryFunction은 1개의 매개 변수를 사용하는 함수를 말합니다. )

 

이 함수 객체가 반환한 값을 result가 가리키는 대상의 범위에 출력합니다.

이때, 대상의 범위는 입력된 원소들을 모두 담을 만큼 커야 합니다.

그리고, 대상 범위와 입력 범위가 일치해도 상관없습니다.

이 경우, 입력 원소가 함수의 결과로 대체됩니다.

 

다음은 int 배열 원소들의 제곱을 계산하여 출력하는 예제입니다.

#include <iostream>
#include <algorithm>
using namespace std;

int compute( int v){
    return v * v;
}

int main(){

    int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int res[10];	// 출력 대상

    transform( arr, arr+ 10, res, compute);

    for( int v : res){	// 범위 기반 for
        cout << v << " ";
    }
}

▼출력

1 4 9 16 25 36 49 64 81 100

 

위 예제에서는 arr 배열로부터 값을 입력받아, 제곱 값을 계산한 후 대상 배열에 출력하고 있습니다.

계산 결과를 출력할 때, 범위 기반 for 구문을 사용했습니다.

범위 기반 for에 관한 자세한 내용은 이곳에 정리해 두었습니다.

 

[C++] 범위 기반 for 루프 (Range-based for loop) 사용법

범위 기반 for범위 기반 for 루프는 배열이나 컨테이너 같이 데이터의 연속을 나타내는 객체의 모든 멤버를 순환하는 구문입니다.이 기능은 C++11부터 도입되었습니다. 선언 방식은 다음과 같습니

codingembers.co.kr

 

다음 예제는 vector의 원소들의 값을 2 증가시키는 예제입니다.

int main(){

    vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    
    // 각 원소에 람다 표현식을 적용
    transform( vec.begin(), vec.end(), vec.begin(), [](int v)->int{ 
        return v + 2;
    } );

    for_each( vec.begin(), vec.end(), [](int v){
        cout << v << " ";
    });
}

▼출력

3 4 5 6 7 8 9 10 11 12

이 함수에선 입력과 대상의 범위가 일치합니다.

이런 경우, 원본의 값이 함수가 적용된 값으로 변경됩니다.

 

여기에선, 람다 표현식( lamda expression )을 사용해서 적용할 함수를 손쉽게 구현했습니다.

람다 표현식에 관한 내용은 여기에서 볼 수 있습니다.

 

[C++] 람다 표현식( lamda expression )에 대한 설명

람다 표현식( lamda expression )이란줄여서 람다( lamda )라고도 하는 람다 표현식은 익명의 함수 객체를 정의하고 사용하기 위한 표기법입니다.이 표현식은 간결한 기능을 구현하는데 너무 많은 손이

codingembers.co.kr

 

그리고, 마지막은 for_each 함수를 사용해서 변경된 원소의 값들을 출력하고 있습니다.

for_each 함수도 입력된 범위의 모든 원소에 대하여 함수 객체를 적용하는 함수입니다.

 

이 함수와 transform은 비슷하게 동작하지만, 분명히 구분되는 차이점 3가지가 있습니다.

첫 번째, transform은 함수가 적용된 결과를 대상 범위에 따로 출력할 수 있다는 것입니다.

for_each 함수는 그렇게 할 수 없습니다.

 

for_each에 관한 내용은 여기에서 볼 수 있습니다.

 

[C++] for_each 함수 사용법

for_each 함수for_each는 주어진 구간 내에 있는 원소들에 대하여 지정한 함수 객체를 적용하는 함수입니다. 이 함수를 사용하려면 다음의 헤더를 포함해야 합니다.#include  함수의 정의는 다음과 같

codingembers.co.kr

 

두 번째, transform은 두 개의 입력 범위를 받아들일 수 있다는 것입니다.

for_each를 사용해서 이런 기능을 구현하기엔 불편한 점이 많이 있습니다.

 

transform의 두 번째 사용법

transform의 두 번째 정의는 다음과 같습니다.

template<class InputIterator1, class InputIterator2, class OutputIterator, class BinaryFunction>
OutputIterator transform(
    InputIterator1 first1,
    InputIterator1 last1,
    InputIterator2 first2,
    OutputIterator result,
    BinaryFunction func );

이 버전의 특징은, 두 개의 입력 범위를 사용해서 결과를 출력할 수 있다는 것입니다.

따라서, func 함수 객체는 두 개의 매개 변수를 받아들입니다.

( BinaryFunction은 2개의 매개 변수를 사용하는 함수를 말합니다. )

 

이 결과를 첫 번째 버전과 같이, 대상 범위에 저장합니다.

 

다음은 두 vector로부터 값들을 입력받아 합한 값을 출력하는 예제입니다.

int main(){

    vector<int> vec1 = { 2, 4, 6, 8, 10 };
    vector<int> vec2 = { 1, 3, 5, 7, 9 };

    vector<int> ret(vec1.size()); // 대상 범위
        
    transform( vec1.begin(), vec1.end(), vec2.begin(), ret.begin(), [](int v1, int v2){ 
        return v1 + v2;
    } );

    for_each( ret.begin(), ret.end(), [](int v){
        cout << v << " ";
    });
}

▼출력

3 7 11 15 19

 

위의 예제를, 함수 객체( Function Object, Functor )를 사용해서 구현할 수도 있습니다.

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>   // 함수 객체 사용
using namespace std;

int main(){

    vector<int> vec1 = { 2, 4, 6, 8, 10 };
    vector<int> vec2 = { 1, 3, 5, 7, 9 };

    vector<int> ret(vec1.size()); // 대상 범위
        
    // std 함수 객체 사용
    transform( vec1.begin(), vec1.end(), vec2.begin(), ret.begin(), plus<int>() );

    for_each( ret.begin(), ret.end(), [](int v){
        cout << v << " ";
    });
}

위 예제에서 plus <int>는 C++ 표준 라이브러리에서 지원하는, 더하기 연산 함수 객체입니다.

이 객체를 사용하려면 다음의 함수를 포함해야 합니다.

#include <functional>

이 헤더 파일은 사칙연산뿐만 아니라 수많은 함수 객체를 작성하여 제공하고 있습니다.

 

함수 객체( function object )에 관한 내용은 여기에서 정리해 두었습니다.

 

[C++] 함수 객체( Function Object )와 operator()

함수 객체( Function Object )함수 객체( Function Object )는 operator()를 구현한 타입을 말합니다.이 함수 객체라는 용어 때문에, 함수 객체라는 타입을 인스턴스화 한 객체와 혼동을 일으키면 안 됩니다.C+

codingembers.co.kr

 

마지막으로, transformfor_each 함수의 세 번째 차이점은,

transform은 지정된 함수 객체가 적용되는 순서가 입력 범위의 원소의 순서와 일치하는 것을 보증하지 않는다는 것입니다.

 

예를 들어, 배열에 1, 2, 3, 4, 5의 원소들이 이 순서로 입력되어 있다고 해봅시다.

이 배열의 원소들에 tranform을 통해 2씩 더하려고 합니다.

 

우리는 일반적으로, 1에 2를 더해서 3이 되고, 그다음 원소 2에 2를 더해서 4가 되고, 원소 3에 2를 더해서 5가 되고...

이런 식으로, 함수가 적용될 것이라 예상하지만, 사실은 이 순서를 장담하지 못한다는 것입니다.

원소 5에 2가 더해지는 계산이 먼저 일어날 수도 있습니다.

 

이런 일이 발생하는 것은 표준 라이브러리의 대부분 알고리즘들이 속도를 위해 병렬 연산을 수행하기 때문입니다.

따라서, 아래와 같이 연산 순서가 중요한 경우 for_each를 사용하는 것이 유리합니다.

int main(){

    vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    
    for_each( vec.begin(), vec.end(), [](int v){    // 원소의 순서대로 출력
        cout << v << " ";
    });
}

 

 

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유