태그 보관물: foreach

foreach

for 루프의 std :: for_each 장점 장점이 있습니까? 나에게

std::for_each오버 for루프의 장점이 있습니까? 나에게 std::for_each코드의 가독성을 방해하는 것 같습니다. 그렇다면 일부 코딩 표준에서 사용을 권장하는 이유는 무엇입니까?



답변

C ++ 11 (이전의 C ++ 0x) 의 좋은 점 은이 지루한 논쟁이 해결 될 것입니다.

전체 컬렉션을 반복하고 싶어하는 올바른 마음을 가진 사람은 여전히 ​​이것을 사용하지 않습니다.

for(auto it = collection.begin(); it != collection.end() ; ++it)
{
   foo(*it);
}

아니면 이거

for_each(collection.begin(), collection.end(), [](Element& e)
{
   foo(e);
});

영역 기반 for루프 구문을 사용할 수있다 :

for(Element& e : collection)
{
   foo(e);
}

이러한 종류의 구문은 Java 및 C #에서 한동안 사용 가능했으며 실제로는 최근에 본 모든 Java 또는 C # 코드에서 foreach기존 for루프 보다 더 많은 루프 가 있습니다.


답변

몇 가지 이유는 다음과 같습니다.

  1. 익숙하지 않거나 읽기 쉬운 도구를 사용하지 않아서 가독성을 방해하는 것 같습니다. (헬퍼의 경우 boost :: range 및 boost :: bind / boost :: lambda를 참조하십시오. 이들 중 다수는 C ++ 0x로 이동하여 for_each 및 관련 함수를보다 유용하게 만듭니다.)

  2. 모든 반복자와 작동하는 for_each 위에 알고리즘을 작성할 수 있습니다.

  3. 어리석은 타이핑 버그의 가능성을 줄입니다.

  4. 또한, 같은 STL-알고리즘의 나머지에 당신의 마음을 열고 find_if, sort, replace, 등 이들은 더 이상 이상한 보이지 않는 것입니다. 이것은 큰 승리가 될 수 있습니다.

업데이트 1 :

가장 중요한 것은, 그것이 for_each존재하는 모든 것과 같은 for-loops를 넘어서서 find / sort / partition / copy_replace_if, parallel execution .. 또는 무엇이든과 같은 다른 STL- 알고리즘을 살펴 보는 것입니다.

for_each의 형제의 “나머지”를 사용하여 많은 처리를 매우 간결하게 작성할 수 있지만 다양한 내부 논리를 사용하여 for-loop를 작성하는 것만으로도 그 사용법을 배우지 못할 것입니다. 바퀴를 반복해서 발명하게됩니다.

(곧 사용 가능한 범위 스타일 for_each) :

for_each(monsters, boost::mem_fn(&Monster::think));

또는 C ++ x11 람다 :

for_each(monsters, [](Monster& m) { m.think(); });

IMO가 다음보다 더 읽기 쉽습니다.

for(Monsters::iterator i = monsters.begin(); i != monsters.end(); ++i) {
    i->think();
} 

또한 이것 (또는 람다와 함께, 다른 사람들을보십시오) :

for_each(bananas, boost::bind(&Monkey::eat, my_monkey, _1));

다음보다 더 간결합니다.

for(Bananas::iterator i = bananas.begin(); i != bananas.end(); ++i) {
    my_monkey->eat(*i);
} 

특히 여러 함수를 순서대로 호출하는 경우 …하지만 아마도 나뿐입니다. 😉

업데이트 2 : 반복자 쌍 대신 범위와 함께 작동하는 자체 stl-algos 래퍼를 작성했습니다. boost :: range_ex가 출시되면 C ++ 0x에도 포함됩니까?


답변

for_each더 일반적입니다. 시작 / 종료 반복자를 전달하여 모든 유형의 컨테이너를 반복하는 데 사용할 수 있습니다. for_each반복 코드를 업데이트하지 않고도 사용하는 함수 아래에서 컨테이너를 교체 할 수 있습니다 . 세상에는 다른 std::vector오래된 컨테이너 와 일반 C 어레이의 이점을 보려면 다른 컨테이너가 있다는 것을 고려해야합니다 for_each.

가장 큰 단점은 for_eachfunctor가 필요하기 때문에 구문이 복잡하다는 것입니다. 이것은 람다를 도입하여 C ++ 11 (이전 C ++ 0x)에서 수정되었습니다.

std::vector<int> container;
...
std::for_each(container.begin(), container.end(), [](int& i){
    i+= 10;
});

3 년 후에는 이상하게 보이지 않을 것입니다.


답변

개인적으로, 사용 방법을 std::for_each벗어나야 할 때마다 (특수 기능 펑터 작성 / 복잡한 작성 boost::lambda) BOOST_FOREACHC ++ 0x의 범위 기반을 사용하여 더 명확하게 알 수 있습니다.

BOOST_FOREACH(Monster* m, monsters) {
     if (m->has_plan()) 
         m->act();
}

vs

std::for_each(monsters.begin(), monsters.end(), 
  if_then(bind(&Monster::has_plan, _1), 
    bind(&Monster::act, _1)));


답변

매우 주관적이며 일부는 동일한 규칙으로 다른 컬렉션을 처리 할 수 ​​있기 때문에 for_each 코드를 읽기 쉽게 만들 것이라고 말합니다 .
for_eachitslef는 루프로 구현됩니다

template<class InputIterator, class Function>
  Function for_each(InputIterator first, InputIterator last, Function f)
  {
    for ( ; first!=last; ++first ) f(*first);
    return f;
  }

그래서 당신에게 맞는 것을 선택하는 것은 당신에게 달려 있습니다.


답변

많은 알고리즘 함수와 마찬가지로 초기 반응은 루프보다 foreach를 사용하는 것이 더 읽기 어렵다고 생각하는 것입니다. 많은 화염 전쟁의 주제였습니다.

관용구에 익숙해지면 유용 할 수 있습니다. 명백한 이점 중 하나는 코더가 루프의 내부 내용을 실제 반복 기능과 분리하도록 강제한다는 것입니다. (OK, 그것이 이점이라고 생각합니다. 다른 사람들은 당신이 실제로 이익을 얻지 않고 코드를 자르고 있다고 말합니다).

다른 장점은 foreach를 볼 때 모든 항목이 처리되거나 예외가 발생한다는 것을 알고 있습니다.

에 대한 루프는 루프를 종료하는 몇 가지 옵션이 있습니다. 루프가 전체 과정을 실행하도록하거나 break 키워드를 사용하여 루프에서 명시 적으로 점프하거나 return 키워드를 사용 하여 전체 함수 중간 루프를 종료 할 수 있습니다. 반대로, foreach 는 이러한 옵션을 허용하지 않으므로 더 읽기 쉽습니다. 함수 이름을 한 눈에 볼 수 있으며 반복의 전체 특성을 알 수 있습니다.

혼란스러운 for 루프 의 예는 다음과 같습니다 .

for(std::vector<widget>::iterator i = v.begin(); i != v.end(); ++i)
{
   /////////////////////////////////////////////////////////////////////
   // Imagine a page of code here by programmers who don't refactor
   ///////////////////////////////////////////////////////////////////////
   if(widget->Cost < calculatedAmountSofar)
   {
        break;
   }
   ////////////////////////////////////////////////////////////////////////
   // And then some more code added by a stressed out juniour developer
   // *#&$*)#$&#(#)$#(*$&#(&*^$#(*$#)($*#(&$^#($*&#)$(#&*$&#*$#*)$(#*
   /////////////////////////////////////////////////////////////////////////
   for(std::vector<widgetPart>::iterator ip = widget.GetParts().begin(); ip != widget.GetParts().end(); ++ip)
   {
      if(ip->IsBroken())
      {
         return false;
      }
   }
}


답변

대부분 정확합니다. 대부분의 경우 std::for_each순 손실입니다. 나는 비교 for_each하기 위해 갈 것입니다 goto. goto가능한 가장 다양한 흐름 제어 기능을 제공합니다. 상상할 수있는 거의 모든 다른 제어 구조를 구현하는 데 사용할 수 있습니다. 그러나 그 다재다능 함 goto은 고립 된 상태 를 보는 것이이 상황에서 의도 한 바에 대해 거의 아무 것도 알려주지 않음을 의미합니다 . 결과적 goto으로, 최후의 수단을 제외하고 는 올바른 마음을 가진 사람은 거의 없습니다 .

표준 알고리즘 중에서도 for_each거의 동일한 방식입니다. 사실상 모든 것을 구현하는 데 사용할 수 있습니다. 즉, for_each이 상황에서 어떤 것이 사용되고 있는지에 대해 전혀 알 수 없습니다. 불행하게도, 사람들의 태도 for_eachgoto1970 년 정도 의 태도 에 관한 것입니다. 소수의 사람들은 최후의 수단으로 만 사용해야한다는 사실에 사로 잡혀 있었지만, 여전히 많은 사람들이 그것을 기본 알고리즘으로 생각합니다. 다른 것을 사용하는 경우는 거의 없습니다. 대부분의 시간, 심지어 한 눈에 볼 때 대안 중 하나가 크게 우수했음을 알 수 있습니다.

예를 들어, 사람들이을 사용하여 컬렉션의 내용을 인쇄하기 위해 코드를 작성하는 것을 본 횟수를 잊어 버렸습니다 for_each. 내가 본 게시물을 기반으로하여 가장 일반적인 단일 사용 일 수 있습니다 for_each. 그들은 다음과 같이 끝납니다 :

class XXX {
// ...
public:
     std::ostream &print(std::ostream &os) { return os << "my data\n"; }
};

그리고 그 이후는 어떤 조합에 대해 요구하고있다 bind1st, mem_fun등 그들이 같은 것을 할 필요가 :

std::vector<XXX> coll;

std::for_each(coll.begin(), coll.end(), XXX::print);

작업하고의 요소를 인쇄하십시오 coll. 실제로 내가 작성한 그대로 정확하게 작동한다면 평범하지는 않지만 작동하지 않을 때 코드와 관련된 몇 가지 코드를 찾기가 어렵습니다. 그것을 붙들고있는 조각들 사이에서 진행됩니다.

다행히도 더 좋은 방법이 있습니다. XXX에 일반 스트림 삽입 기 과부하를 추가합니다.

std::ostream &operator<<(std::ostream *os, XXX const &x) {
   return x.print(os);
}

그리고 사용 std::copy:

std::copy(coll.begin(), coll.end(), std::ostream_iterator<XXX>(std::cout, "\n"));

그 작업을 수행 -하고의 내용을 인쇄하는 것을 알아 내기 위해 모두에서 실질적으로 일을지지 않습니다 coll에를 std::cout.