범위 내의 데이터 값을 요약하거나 더하는 데에는 accumulate 나 for_each를 사용하자

STL 에서는 아래와 같이 특정 범위에 대한 연산을 준비해 놓고 있다.














count 범위 내의 요소 개수를 센다.
count_if 조건에 맞는 요소 개수를 센다.
min_element 범위내의 최소값을 얻는다.
max_element 범위내의 최대값을 얻는다.

하지만 위의 네가지 알고리즘 보다 좀더 융통성 있는 무언가를 해야 할 경우가 있다. 이 경우 accumulatefor_each 를 사용 할수 있다. 예를 들어 double 형 리스트의 총합을 알고 싶을 경우 accumulate를 아래와 같이 사용할 수 있다.
list<double> ld;
….
double sum = accumulate(ld.begin(), ld.end(). 0.0);
와 같은 형태로 쓸수 있다. (마지막 인자인 초기값에 주의. 0으로 초기값을 줄 경우 accumulate는 내부적으로 int형으로 동작되어 double 형의 값이 잃어버릴수 있다.) 위와 같은 수치형에서의 간단한 누적 합이외에 특정 클래스의 컨테이너 범위에서 클래스의 요약정보를 얻기 위해서도 사용이 가능하다.
예를 들어 컨테이너 안에 들어있는 string 객체의 문자열 길이의 합을 계산하는데 사용가능하다.
string::size_type
stringLengthSum(string::size_type sumSoFar, const string& s)
{
return sumSoFar+s.size();
}
stringLengthSum은 accumulate가 받아들이는 요약용 함수의 전형이다. 이 함수는 바로 전까지 요약된 값과 현재의 요소를 받아 들여서 새 요약값을 반환한다. 즉 컨테이너안의 string 사이즈의 요약 정보를 얻을때 string::size_type sumSoFar는 이전 까지의 항목까지의 요약 정보고 현재 항목의 string 객체를 받는다. 그리고 다시 요약한 정보를 리턴한다.
set<string> ss;

string::size_type lengthSum =
accumulate(ss.begin(), ss.end(), 0, stringLengthSum);
위와 같은 식으로 사용이 가능하다. 범위 내의 수를 곱하는 것은 더욱 쉽다. STL에 해당 함수가 존재하기 때문이다.
vector<float> vf;

float product =
accumulate(vf.begin(), vf.end(), 1.0, multiplies<float>() );
(곱셉연산이기 때문에 1.0 으로 시작한다.)
사용자 클래스에 대한것 역시 가능하다.
// 사용자 클래스
struct Point{
Point(double initX, double initY): x(initX), y(initY) {}
double x,y;
}

// 적용될 함수자. 자세한 내용은 EffectiveSTL40을 참조
class PointAverage : public binary_function<Point, Point, Point> {
public:
PointAverage() : xSum(0), ySum(0), numPoints(0) {}
const Point operator() (const Point& avgSoFar, const Point& p)
{
++numPoints;
xSum += p.x;
ySum += p.y;
return Point(xSum/numPoint, ySum/numPoint);
}

private:
size_t numPoints;
double xSum;
double ySum;
};

// 실제 사용예
list<Point> lp;

Point avg =
accumulate(lp.begin(), lp.end(), Point(0,0) PointAverage());

PointAverage는 자인이 계산에 사용한 Point객체의 개수와 x,y 좌표의 합을 내부적으로 유지해 가며 동작한다. 하지만 위의 PointAverage은 표준안의 26.4.1절의 두번째 단락의 내용인 accumulate에 넘겨지는 함수의 부가적 효과는 없어야 한다에 어긋나는 클래스이다.(실질적으로 문제가 생기지는 않지만 어쩌면 정의되지 않는 동작이 일어날수 있다고 한다.)
위의 문제를 해결하기 위해 for_each를 사용할 수 있다. 이 알고리즘은 범위 내의 데이터를 요약할 수 있는 알고리즘이면서 accumulate가 가진 제한을 받지 않는다. for_each는 범위와 그 범위 내의 요소에 대해 호출할 함수를 받아들이는데, 이 알고리즘에 넘겨지는 함수는 자신이 처리할 단 하나의 요소만을 받아들이며, for_each는 자신의 수행을 마칠때 이 함수의 사본을 반환한다.


// EffectiveSTL 40 참조
class PointAverage : public unary_function<Point, void> {
public:
PointAverage() : xSum(0), ySum(0), numPoints(0) {}
void operator()(const Point& p )
{
++numPoints;
xSum += p.x;
ySum += p.y;
}

Point result() const
{
return Point(xSum/numPoints, ySum/numPoints);
}

private:
size_t numPoints;
double xSum;
double ySum;
};

// 사용예
list<Point> lp;

Point avg = for_each(lp.begin(), lp.end(), PointAverage()).result();

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다