일반적으로 알고리즘은 동작을 실행할 객체 범위의 시작과 끝을 가리키는 반복자를 받음

따라서 알고리즘은 매개 변수로 지정된 범위 안에 있는 모든 객체를 순회해야 하므로 루프를 수행함

 

루프를 통해 Widget::redraw를 실행한다고 가정하자

list<Widget> lw;
const list<Widget>::iterator endIter = lw.end();
for(list<Widget>::iterator iter = lw.begin(); iter != endIter; ++iter)
{
    iter->redraw();
}

 

for_each 알고리즘으로 똑같이 구현할 수 있음

for_each(v.begin(), v.end(), [](Widget& widget) { widget.redraw(); });

 

루프보다 for_each 한 번의 알고리즘 호출이 일반적으로 좋을 수 있음

효율

라이브러리 제작자가 컨테이너의 구현 방식을 충분히 파악하고 있기 때문에 순회 코드를 최적화함

예를 들면 deque는 하나 이상의 고정 크기 배열에 저장되므로 반복자보다 포인터를 통해 순회하는 것이 빠르다는 것을 라이브러리 제작자는 알고 있기 때문에 최적화가 이루어짐

하지만 라이브러리 사용자는 최적화 내용을 모르기 때문에 루프보다 알고리즘의 효율이 더 좋을 수 있음

 

컴퓨터 공학적인 알고리즘을 사용하여 보통 프로그래머보다 높은 수준의 효율적인 알고리즘을 제공

 

정확성

반복자가 유효해야 하고, 원하는 위치를 가리켜야 함

프로그래머가 반복자를 잘못 조작할 수 있고 무효화될 수 있음

 

배열의 모든 요소마다 41을 더한 후 deque의 앞에 삽입한다고 가정하자

계속 앞에 삽입하므로 배열의 순서가 반대되는 상황이 발생

double data[maxNumDoubles];
deque<double> d;

for(size_t index = 0; index < maxNumDoubles; ++index)
{
    d.insert(d.begin(), data[i] + 41);
}

 

순서가 바뀌는 상황은 수정했지만 insert가 호출되면서 모든 반복자가 무효화됨

deque<double>::iterator insertIter = d.begin();

for(size_t index = 0; index < maxNumDoubles; ++index)
{
    d.insert(insertIter++, data[i] + 41);
}

 

반복자 무효화 수정

deque<double>::iterator insertIter = d.begin();

for(size_t index = 0; index < maxNumDoubles; ++index)
{
    insertIter = d.insert(insertIter, data[i] + 41);
    ++insertIter;
}

 

알고리즘으로 사용하면 무효화 걱정이 필요 없음

transform(data, data + maxNumDoubles, inserter(d, d.begin()), [](double a) { return a + 41; });

 

유지보수성

직접 만든 루프보다 간단한 코드로 생산될 가능성이 높음

알고리즘의 함수 이름이 각각의 동작을 잘 정의되어 있어서 충분히 이해할 수 있는 수준이므로 명확한 의미를 전달

 

만약 루프로는 간단한 작업인데, 알고리즘으로는 복잡하다면 루프가 더 좋음

+ Recent posts

목차