안녕하세요.
오늘은 <algorithm> 헤더에 정의되어 있는 STL 알고리즘인 std::transform() 함수에 대해 알아보겠습니다.
transform() 함수는 3개의 반복자와 1개의 단항 연산 함수를 매개변수로 갖습니다.
( 바이너리 연산 함수를 인자로 갖기도 하지만 여기선 단항 연산 함수만 다루겠습니다. )
template< class InputIt, class OutputIt, class UnaryOperation >
OutputIt transform( InputIt first1,
InputIt last1,
OutputIt d_first,
UnaryOperation unary_op
);
: transform() 함수는 [first1, last1) 범위의 각 요소마다 함수를 호출하고 그 결과를 d_first 위치에 추가합니다.
first1, last1
Input 컨테이너의 범위를 나타냅니다.
d_first
각 요소의 결과 값인 output이 추가될 위치를 나타냅니다.
unary_op
단항 연산 함수입니다.
Input 컨테이너의 각 요소들이 해당 함수를 거쳐 d_first 위치에 추가됩니다.
예제로 이해하는 것이 제일 빠르니 예제로 살펴보겠습니다!
#include <iostream>
#include <string>
#include <vector>
#include <algorithm> // std::transform()
#include <iterator> // std::back_inserter()
using std::string;
using std::vector;
using std::cout;
// myvector의 모든 요소에 순차적으로 2를 곱연산하여
// output 컨테이너의 끝에 순차적으로 추가합니다.
int main(void)
{
// 1 2 3 4 5
vector<int> myvector {1, 2, 3, 4, 5};
// 10 10 10 10
vector<int> output(4, 10);
/*
1. myvector의 [begin, end) 범위의 각 요소를 람다 함수의 인자 n에 전달합니다.
2. 각 결과마다 output
ex) myvector의 첫 요소인 1에 2를 곱하여 추가, 다음 요소인 2에 2를 곱하여 추가..
*/
std::transform(myvector.begin(), myvector.end(), std::back_inserter(output),
[](int n) { return n*2; });
// 10 10 10 10 2 4 6 8 10
for (vector<int>::const_iterator iter = output.begin(); iter != output.end(); ++iter)
{
cout << *iter << " ";
}
cout << "\n";
return 0;
}
결과)
설명)
위 프로그램은 myvector의 모든 요소에 순차적으로 2를 곱연산하여 output 컨테이너의 끝에 순차적으로 추가합니다.
transform() 함수의 동작을 살펴보면 먼저 처음 2개의 인수로 주어진 [ myvector.begin(), myvector.end() ) 범위가 유효한지 확인합니다.
다음으로 위 범위의 각 요소에 람다식을 적용하여 2를 곱연산 한 값을 3번째 인수인 std::back_inserter(output)이 반환하는 위치에 추가합니다. 반복자를 증가시켜 다음 요소에도 똑같은 동작을 반복합니다.
이 때 주의할 점은 transform() 함수가 결과 값을 추가할 저장 공간을 확보해주지 않습니다. 무슨 말이냐면 만약 vector의 capacity가 0일 때 push_back과 같은 함수가 아닌 단순히 인덱스를 사용하여 vec[0] = 3 과 같이 대입('=')을 한다면 유효하지 않은 공간에 접근하기 때문에 런타임 에러가 납니다. 아래 1312 라인을 보면 transform 함수가 요소를 추가할 때 push_back()과 같은 연산이 아닌 '=' 연산자를 사용하여 값을 대입합니다.
따라서 추가할 위치의 메모리 공간이 유효하지 않다면 런타임 에러를 발생시킵니다. 즉 추가하려는 위치의 공간이 미리 확보가 되어있어야 합니다. 혹은 push_back 함수처럼 자동으로 공간을 확보하며 추가할 수 있도록 해주어야 합니다.
이것을 가능하게 해주는 것이 바로 예제에서 사용한 back_insert_iterator 타입을 반환하는 back_inserter() 함수입니다.
back_inserter 함수는 컨테이너를 받아 back_insert_iterator 타입의 객체를 생성하여 반환합니다. back_insert_iterator 클래스는 이름 그대로 뒤에 삽입을 위한 일종의 반복자 클래스입니다. 이 클래스는 내부적으로 대입(=) 연산자 오버로딩이 되어있습니다. (copy assignment 아님)
그 내용을 살펴보면 아래와 같이 '=' 연산을 통해 back_insert_iterator 타입의 변수에 값을 대입할 경우 push_back() 함수로 값을 추가해주고 있습니다. 즉 외부에서 '='을 통해 대입을 하면 내부적으로 push_back으로 변경하여 추가하기 때문에 공간 확보에 대한 신경을 써주지 않아도 된다는 뜻입니다.
참고)
https://en.cppreference.com/w/cpp/algorithm/transform
https://docs.microsoft.com/ko-kr/cpp/standard-library/back-insert-iterator-class?view=vs-2019
'C,C++ > C++' 카테고리의 다른 글
[C++ STL] <algorithm> std::search() 함수 (0) | 2019.11.07 |
---|---|
[C++ STL] 특정 값 제외 컨테이너 복사 std::remove_copy() 함수 (0) | 2019.11.06 |
[C++] std::equal 함수로 간단하게 회문 판별 함수 구현해보기 / palindrome (0) | 2019.11.02 |
[C++] 공백 단위로 문자열 자르는 split 함수 구현 ( find_if() 사용 ) (0) | 2019.11.01 |
[C++] 컨테이너 복사 std::copy 함수, 반복자 어댑터 std::back_inserter (1) | 2019.10.26 |
댓글