42-2-나.요소 생성

요소 생성 함수는 새로운 요소를 만들어 지정한 위치에 대입하는 함수들이다. 요소를 만드는 것은 맞지만 컨테이너에 삽입하는 것이 아니라 기존 요소를 파괴하고 대입한다는 점을 주의하도록 하자. 다음 함수는 반복자 구간을 val 값으로 가득 채운다.

 

void fill(FwdIt first, FwdIt last, const T& val);

void fill_n(OutIt first, Size n, const T& val);

 

fill은 반복자 구간을 지정하는데 비해 fill_n은 시작 위치와 개수를 지정한다는 점이 다르다. 반복자 구간은 이미 메모리가 확보되어 있어야 한다. 만약 생성된 값을 꼭 삽입하려면 삽입 반복자를 사용해야 하며 이때는 두 번째 원형만 사용할 수 있다.

 

: fill

#include <iostream>

#include <vector>

#include <algorithm>

using namespace std;

 

template<typename C> void dump(const char *desc, C c) { cout.width(12);cout << left << desc << "==> ";

      copy(c.begin(),c.end(),ostream_iterator<typename C::value_type>(cout," ")); cout << endl; }

 

void main()

{

     int ari[]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};

     vector<int> vi(&ari[0],&ari[16]);

 

     fill(vi.begin()+2,vi.end()-5,99);

     dump("vi",vi);

}

 

정수값을 가진 벡터의 일정 구간을 99로 채웠다. 구간내의 모든 값을 99로 변경하는 것이다.

 

vi          ==> 1 2 99 99 99 99 99 99 99 99 99 12 13 14 15 16

 

fill_n(vi.begin()+2,5,99); 이렇게 수정하면 2번째부터 5개의 요소가 99로 바뀐다. 컨테이너의 모든 요소에 대해 일괄적으로 대입하고 싶을 때 이 함수를 사용한다. 다음 함수는 반복자 구간의 요소들을 무작위로 마구 섞는다.

 

void random_shuffle(RanIt first, RanIt last[, UniOp& op]);

 

반복자 구간은 물론 유효해야 한다. 이 함수는 난수로 요소를 생성해서 대입하는 것이 아니라 이미 존재하는 값들의 순서를 난수로 변경함으로서 섞는다.

 

: random_shuffle

#include <Turboc.h>

#include <iostream>

#include <algorithm>

using namespace std;

 

void main()

{

     char str[]="abcdefghijklmnopqrstuvwxyz";

    

     randomize();

     puts(str);

     random_shuffle(&str[0],&str[strlen(str)]);puts(str);

     random_shuffle(&str[0],&str[strlen(str)]);puts(str);

     random_shuffle(&str[0],&str[strlen(str)]);puts(str);

}

 

알파벳이 순서대로 들어가 있는 배열을 이 함수로 섞어 보았다. 무작위 난수를 사용했으므로 실행 결과는 매번 달라질 것이다.

 

abcdefghijklmnopqrstuvwxyz

etarkmilsygqhojxuzwpnbfdvc

oabsrvdiepmzknhugjcxywltfq

blvuserzyfgknjcoaxmpqhiwdt

 

난수를 발생시키는 생성기 함수 객체를 지정할 수도 있지만 대부분의 경우 디폴트 난수 발생기를 사용해도 고르게 잘 섞으므로 굳이 생성기를 제공할 필요는 없다. 생성기 함수는 난수 범위의 상한을 지정하는 정수를 인수로 받아 0~인수 미만의 난수를 발생시켜야 한다. 게임판을 무작위로 섞고자 할 때 흔히 사용된다. 다음 함수는 반복자 구간에 대해 g 함수를 호출하여 리턴되는 값으로 채운다.

 

void generate(FwdIt first, FwdIt last, Gen g);

void generate_n(OutIt first, Dist n, Gen g);

 

fill 함수와 마찬가지로 반복자 구간을 인수로 전달받을 수도 있고 시작 위치와 개수를 인수로 받을 수도 있다. g는 인수를 받지 않고 컨테이너 요소 타입을 리턴하는 함수 객체이다. 다음 예제는 피보나치 수열을 만든다.

 

: generate

#include <iostream>

#include <vector>

#include <algorithm>

using namespace std;

 

template<typename C> void dump(const char *desc, C c) { cout.width(12);cout << left << desc << "==> ";

      copy(c.begin(),c.end(),ostream_iterator<typename C::value_type>(cout," ")); cout << endl; }

 

int fibo()

{

     static int i1=1,i2=1;

     int t;

     t=i1+i2;

     i1=i2;

     i2=t;

     return t;

}

 

void main()

{

     vector<int> vi(10);

 

     generate(vi.begin(),vi.end(),fibo);

     dump("vi",vi);

}

 

크기 10의 정수 벡터를 만들고 이 벡터를 피보나치 수열로 채웠다.

 

vi          ==> 2 3 5 8 13 21 34 55 89 144

 

피보나치 수열은 이 외에도 다양한 방법으로 만들 수 있다. 생성기 함수 객체는 별도의 인수를 받아들이지 않으므로 매번 다른 값을 만들기 위해서는 함수 자체가 별도의 정보를 저장하고 있어야 한다. 예제의 fibo 함수는 직전의 수 둘을 정적변수에 저장하고 있는데 멀티 스레드 환경에서는 동기화 문제가 발생할 수 있다. 이럴 때는 함수 객체를 만들어야 한다.

 

struct fibo {

private:

     int i1,i2;

public:

     fibo() : i1(1),i2(1) { }

     int operator()() {

          int t;

          t=i1+i2;

          i1=i2;

          i2=t;

          return t;

     }

};

generate(vi.begin(),vi.end(),fibo());

 

함수 객체는 작업 결과를 스스로 저장할 수 있으며 매 호출마다 지역적으로 생성될 수 있으므로 멀티 스레드 환경에서도 잘 동작한다.