스토리텔링 개발자

[Effective STL] 37. 범위 요약(summarize) 본문

개발/Effective STL

[Effective STL] 37. 범위 요약(summarize)

김디트 2025. 1. 2. 11:19
728x90

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

 

 

 

범위 요약(summarize)
  • 예를 들자면
    • 컨테이너 string 요소들의 길이의 총합을 구한다.
    • 주어진 범위의 숫자들의 곱을 구한다.
    • 좌표 등 2차원 이상의 값들의 평균을 구한다.
  • <numeric> 헤더에 있는 수치 알고리즘인 accumulate을 사용하자.  

 

 

 

accumulate
  • accumulate의 첫 번째 형태
    • 두 개의 반복자와 초기값을 인자로 받는다.
    • 범위 내의 값의 합 + 초기 값을 반환한다.
list<double> ld;
...
double sum = accumulate(ld.begin(), ld.end(), 0.0);
// 0.0을 초기값으로 주었기에, 합을 double로 하여 정상값이 리턴된다.

double sum = accumulate(ld.begin(), ld.end(), 0);
// 0을 초기값으로 주면, 합을 int로 하므로 원하는 값이 리턴되지 않는다.
  • accumulate는 입력 반복자기만 하면 동작하므로 istream_iterators와 istreambuf_iterator(항목 29 참조)에 대해서도 적용 가능하다.
// cin으로 입력받은 값의 합들을 출력한다.
cout << "The sum of the ints on the standard input is "
     << accumulate(istream_iterator<int>(cin), istream_iterator<int>(), 0);
  • accumulate의 두 번째 형태
    • 기존의 형태에 추가로 요약용 함수를 인자로 받는다.
string::size_type stringLengthSum(string::size_type sumSoFar, const string& s)
{
    return sumSoFar + s.size();
}
// 컨테이너::size_type은 size_t를 예쁘게 포장한 것이다.

set<string> ss;
...
// stringLengthSum 함수를 통한 요소값들 + 초기값을 리턴한다.
string::size_type lengthSum = accumulate(ss.begin(), ss.end(), 0, stringLengthSum);
  • 두 번째 형태의 다양한 예시
// 범위 내의 수를 곱한다.
vector<float> vf;
...
float product = accumulate(vf.begin(), vf.end(), 1.0, multiplies<float>());
// 범위 안에 있는 point들의 평균 좌표를 찾는다.
struct Point
{
    Point(double initX, double initY) : x(initX), y(initY) {}
    double x, y;
};

class PointAverage
{
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 / numPoints, ySum / numPoints);
    }
private:
    size_t numPoints;
    double xSum;
    double ySum;
};

list<Point> lp;
...
Point avg = accumulate(lp.begin(), lp.end(), Point(0, 0), PointAverage());
  • 사이드 이펙트 문제를 해결하기 위해 for_each를 써보자.

 

 

 

for_each
  • 이 알고리즘에 넘겨지는 함수는 자신이 처리할 단 하나의 요소만을 받아들이며,
    • for_each의 수행을 마칠 때 이 함수의 사본을 반환한다.
    • 즉, for_each에 넘겨지는, 반환되는 함수는 사이드 이펙트를 가져도 된다.
  • accumulate와의 두 가지 차이
    • accumulate에 비해 for_each라는 이름이 '요약한다'는 기능을 확실히 대변하지 않는다.
    • accumulate는 요약 결과를 바로 반환하지만, for_each는 함수 객체를 반환하므로 이 객체에서 요약 정보를 뽑아내야 한다.
// 범위 안에 있는 point들의 평균 좌표를 찾는다.
struct Point
{
    Point(double initX, double initY) : x(initX), y(initY) {}
    double x, y;
};

class PointAverage
{
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();

 

728x90
Comments