transform(), toupper 함수

출처 : https://www.cplusplus.com/


  • Anagram을 만들다 transform(), remove(), erase() 함수들에 대해 알게 되었다. 각 함수들에 대해 정리하고자 한다.



1. std::transform() -> function template -> #include < algorithm >이 필요

  1. unary operation(단항으로 작동)하는 경우 -> 연산에 항이 한개만 쓰이는 경우
template <class InputIterator, class OutputIterator, class UnaryOperation>
  OutputIterator transform (InputIterator first1, InputIterator last1, OutputIterator result, UnaryOperation op);  
  • 템플릿 함수로 되어 있다. 파라미터로 자료형을 자유롭게 넣을 수 있는데, iterator를 자료형으로 넣으라고 되어 있다.
  • 파라미터들의 자료형으로는 InputIterator, OutputIterator, UnaryOperation가 있다.
  • InputIterator에 들어올 값이 4번째 파라미터에 들어갈 값이 있는 장소(주소)이고, OutputIterator에 들어올 값이 연산결과가 저장될 장소(주소)이다.
  • UnaryOperation는 연산을 진행할 함수가 있는 장소(주소)이다.

  • 4번째 파라미터로 넣을 수 있는 값이 독특하다. op에 넣을 수 있는 것은 함수 포인터거나 함수 객체이다. transform할 내용이 들어가는 부분이다.


  • 작동 방식 : 첫번째와 두번째 파라미터인 [first1, last1) first1 <= x < last1 사이를 돌면서 4번째 파라미터에 넣은 함수 포인터 혹은 함수 객체에 의해 값이 변화된다. 이때, 함수 포인터 혹은 함수 객체에 들어가는 값은 첫번째와 두번째 파라미터에 넣어줬던 이터레이터가 들어가게 된다(위에서 자료형이 InputIterator인 것들). 그 후 3번째 파라미터에 넣은 이터레이터에 앞서 변경한 값을 대입해준다.


예제

#include <iostream>
#include <algorithm> // std:: transform
#include <vector>

using namespace std;

int op_increase(int i) { return ++i; }

int main()
{
	vector<int> foo;
	vector<int> bar;

	for (int i = 1; i < 6; ++i)
		foo.push_back(i * 10);

	bar.resize(foo.size());

	transform(foo.begin(), foo.end(), bar.begin(), op_increase);

	cout << "inside of bar" << endl;
	for (vector<int>::iterator it = bar.begin(); it != bar.end(); ++it)
		cout << *it << "  ";

	return 0;
}

결과 transform1


  1. vector 로 foo 안에 10, 20, 30, 40, 50 이 들어가 있다.
  2. transform(foo.begin(), foo.end(), bar.begin(), op_increase); 를 보면 첫번째와 두번째 인자로 foo의 iterator를 넣었음을 알 수 있다. .begin() 부터 .end()까지 넣었으므로 모든 foo 원소에 대해 transform이 작동할 것이다.
  3. 4번째 인자로 함수의 주소를 넣어주었다(함수의 이름이 곧 함수의 시작 주소). op_increase 함수는 인자 i를 받아 1증가 시킨 후 증가시킨 값을 return 한다.
  4. foo.begin()에 따라 foo안에 있는 값 10이 op_increase 함수에 전달된다. 전달되어진 10은 11이 되어 return 된다.
  5. return된 값은 3번째 파라미터인 bar.begin()에 들어가 11이 bra에 저장된다.
  6. foo.begin() + 1로 넘어가 foo의 다음 원소인 21이 op_increase 함수에 전달된다. 전달되어진 20은 21이 되어 return 된다.
  7. return된 값은 3번째 파라미터인 bar.begin() 다음 장소인 bar.begin() + 1에 들어가 21이 bar에 저장된다.
  8. 위와 같은 행위가 반복되서 vector bar를 출력하면 11, 21, 31, 41, 51이 출력된다. __주의__ : __함수에 의해 return된 값이 bar에 저장되는 것이기 때문에 foo 안에 들어가 있는 값인 10, 20, 30, 40, 50의 값이 변경되는 것은 아니다.__



  1. binary operation(이항 연산)으로 작동하는 경우 -> 연산에 항이 두개 쓰이는 경우
    template <class InputIterator1, class InputIterator2, class OutputIterator, class BinaryOperation>
      OutputIterator transform (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2,
     OutputIterator result, BinaryOperation binary_op);
    
  • 인자가 많지만 자료형은 4종류이다. InputIterator1, InputIterator2, OutputIterator, BinaryOperation다.
  • InputIterator1는 내가 범위를 정할 때 쓰는 이터레이터고 OutputIterator는 함수 포인터나 함수객체를 거친 값이 저장될 공간의 주소이다.
  • InputIterator2와 BinaryOperation가 특징적이다.
  • 위에서는 함수가 op_increase와 같이 인자를 한 개 밖에 받지 못했다. 하지만 InputIterator2와 BinaryOperation를 쓸 경우 함수는 인자를 2개 받아야 하며, 그 인자로 InputIterator1와 InputIterator2의 값이 들어간다.



예제

#include <iostream>
#include <algorithm> // std:: transform
#include <vector>

using namespace std;

int op_increase_double(int i, int j) { return i + j; }

int main()
{
	vector<int> foo;
	vector<int> bar;

	for (int i = 1; i < 6; ++i)
		foo.push_back(i * 10);

	for (int i = 1; i < 6; ++i)
		bar.push_back(i);


	transform(foo.begin(), foo.end(), bar.begin(), foo.begin(), op_increase_double);

	cout << "inside of bar" << endl;
	for (vector<int>::iterator it = bar.begin(); it != bar.end(); ++it)
		cout << *it << "  ";

	cout << endl;

	cout << "inside of foo" << endl;
	for (vector<int>::iterator it = foo.begin(); it != foo.end(); ++it)
		cout << *it << "  ";

	return 0;
}


결과 transform예제2


  • foo는 원소로 10, 20, 30, 40, 50을 가지고 bar는 원소로 1, 2, 3, 4, 5를 갖는다.

  • InputIterator1 first1, InputIterator1 last1으로 foo.begin()과 foo.end()가 들어갔다. 즉 foo의 모든 원소가 연산의 대상이다

  • 특이한 점은 InputIterator2로 bar.begin()이 들어갔다는 점이다. bar.begin()이 들어갔으므로 bar의 모든 원소가 연산의 대상이다.

  • 4번째 인자로 다시 foo.begin()가 들어갔다. 이는 연산의 결과를 foo의 맨 처음부터 저장하겠다는 뜻이다.

  • 5번째 인자로 들어간 op_increase_double 함수를 살펴보자. 인자가 2개 들어갔으므로 함수 또한 인자를 2개 받는다. i와 j를 인자로 받아 둘은 더한 값을 return해준다.

  • 작동방식 : foo에서 10이, bar에 1이 op_increase_double 함수에 인자로 넘어간다. 함수가 둘은 더한 11을 return하면 4번째 인자에 적힌 foo.begin() 자리에 11이란 값이 적힌다.

그 다음으로 foo에서 20이 bar에서 2가 op_increase_double 함수에 인자로 넘어간다. 함수가 둘은 더한 22을 return하면 4번째 인자에 적힌 foo.begin() + 1 자리에 22이란 값이 적힌다.

  • 위와 같은 과정이 반복되어 최종적으로 foo에는 11 22 33 44 55란 값이 저장되게 된다.



2. toupper -> function

int toupper ( int c );
  • 기능은 단순하다. 파라미터 c에 들어온 소문자를 대문자로 바꿔준다. 만약 소문자에 대응하는 대문자가 여러개라면 대문자로 바꾸지 않고 그냥 소문자 c를 그대로 return 한다. 알파벳의 경우 소문자와 대문자는 1 : 1 대응하므로 a, b, c, d, e, f, g … -> A, B, C, D, E, F, G로 바뀔 것이다.


예제

int main ()
{
  int i=0;
  char str[]="Test String.\n";
  char c;
  while (str[i])
  {
    c=str[i];
    putchar (toupper(c));
    i++;
  }
  return 0;
}

==> 실행결과 소문자가 모두 대문자가 되어 TEST STRING이 나오게 된다.



3. 그렇다면 Anagram을 판별하는 함수를 만들 때 위 함수가 어떻게 쓰이는가?


bool IsAnagram(string str1, string str2)
{
	transform(str1.begin(), str1.end(), str1.begin(), toupper);

	transform(str2.begin(), str2.end(), str2.begin(), toupper);
}

anagram을 판별할 때, 알파벳들의 대/소문자는 중요하지 않다. 따라서 입력받은 str1과 str2를 전부 대문자로 바꾼 후에 다시 str1과 str2에 넣어주는 작업을 거친다. 이 때 transform 함수와 toupper 함수를 활용한다.
str1.begin()부터 str1.end()까지 (결국 str1 전체 문자열을) 한 글자씩 차례로 toupper 함수에 넣어 return 받은 값(소문자 -> 대문자)을 다시 str1.begin()부터 str1 끝까지 넣어준다는 뜻이다.