ProgramingTip

"마지막을 사용하고 있습니다"(또는 "연속 된 요소 쌍 사이")에 대한 관용구

bestdevel 2020. 10. 18. 18:51
반응형

"마지막을 사용하고 있습니다"(또는 "연속 된 요소 쌍 사이")에 대한 관용구


누구나 언젠가는이 문제가 발생합니다.

for(const auto& item : items) {
    cout << item << separator;
}

... 그리고 마지막에 말하지 않는 구분 기호를 얻습니다. 인쇄하지 않고 다른 작업을 수행하지만 동일한 유형의 연속 작업에는 구분 기호 작업이 필요하지만 마지막 작업은

이제 구식 for 루프와 배열로 작업한다면

for(int i = 0; i < num_items; i++)
    cout << items[i];
    if (i < num_items - 1) { cout << separator; }
}

(또는 루프의 마지막 항목을 특수 케이스로 수행 할 수 있습니다.) 크기를 모르더라도 비파괴 반복을 허용하는 것이 좋습니다 다음을 수행 할 수 있습니다.

for(auto it = items.cbegin(); it != items.cend(); it++) {
    cout << *it;
    if (std::next(it) != items.cend()) { cout << separator; }
}

나는 마지막 두 가지의 미학을 싫어하고 ranged for 루프를 좋아합니다. 마지막 두 가지와 동일한 효과를 더 멋진 C ++ 11ish 구문을 사용할 수 있습니까?


추가 질문 (이상, 말, 확장 가능성 이있는 하나를 명시 적으로 특별한 경우 첫 번째 또는 마지막 요소가에), 나는 없습니다. 그것은 내가 귀찮게하고 싶지 않은 "구현 세부 사항"입니다. 따라서 상상의 미래 C ++에서는 다음과 같이 될 수 있습니다.

for(const auto& item : items) {
    cout << item;
} and_between {
    cout << separator;
}

내 방식 (추가 지점 없음)은 다음과 달라집니다.

const auto separator = "WhatYouWantHere";
const auto* sep = "";
for(const auto& item : items) {
    std::cout << sep << item;
    sep = separator;
}

은 알고 계십니까 당신 더프의 장치를 ?

int main() {
  int const items[] = {21, 42, 63};
  int const * item = items;
  int const * const end = items + sizeof(items) / sizeof(items[0]);
  // the device:
  switch (1) {
    case 0: do { cout << ", ";
    default: cout << *item; ++item; } while (item != end);
  }

  cout << endl << "I'm so sorry" << endl;
  return 0;
}

(라이브)

모두의 하루를 망치지 갑자기 으면 좋겠습니다. 중 하나를 둘 원하지 않으면 절대 사용 하지 마십시오.

(중얼 중얼) 미안해 ...


빈 용기 (범위)를 처리하는 장치 :

template<typename Iterator, typename Fn1, typename Fn2>
void for_the_device(Iterator from, Iterator to, Fn1 always, Fn2 butFirst) {
  switch ((from == to) ? 1 : 2) {
    case 0:
      do {
        butFirst(*from);
    case 2:
        always(*from); ++from;
      } while (from != to);
    default: // reached directly when from == to
      break;
  }
}

라이브 테스트 :

int main() {
  int const items[] = {21, 42, 63};
  int const * const end = items + sizeof(items) / sizeof(items[0]);
  for_the_device(items, end,
    [](auto const & i) { cout << i;},
    [](auto const & i) { cout << ", ";});
  cout << endl << "I'm (still) so sorry" << endl;
  // Now on an empty range
  for_the_device(end, end,
    [](auto const & i) { cout << i;},
    [](auto const & i) { cout << ", ";});
  cout << "Incredibly sorry." << endl;
  return 0;
}

반복에서 끝 요소를 제외하는 범위 제안이 쉽게 만들도록 제안하는 것입니다. (문자열 결합의 특정 작업을 해결하는 더 좋은 방법이 있습니다. 반복에서 요소를 분리하면 컬렉션이 이미 비어있는 경우와 같이 걱정하면 더 특별한 경우가 생성됩니다.)

표준화 된 범위 패러 다 신뢰할 수있는 동안 약간의 도우미 클래스를 사용하여 기존 범위에서 수행 할 수 있습니다.

template<typename T> struct trim_last
{
    T& inner;

    friend auto begin( const trim_last& outer )
    { using std::begin;
      return begin(outer.inner); }

    friend auto end( const trim_last& outer )
    { using std::end;
      auto e = end(outer.inner); if(e != begin(outer)) --e; return e; }
};

template<typename T> trim_last<T> skip_last( T& inner ) { return { inner }; }

이제 쓸 수 있습니다

for(const auto& item : skip_last(items)) {
    cout << item << separator;
}

스템 : http://rextester.com/MFH77611

skip_lastranged-for와 함께 작동 신호 양방향 반복기가 필요합니다. 유사 skip_first하게 기가 충분합니다.


나는 이것에 대한 특별한 관용구를 모른다. 그러나 나는 먼저 특별한 경우를 선호하고 나머지 항목에 대해 작업을 수행합니다.

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> values = { 1, 2, 3, 4, 5 };

    std::cout << "\"";
    if (!values.empty())
    {
        std::cout << values[0];

        for (size_t i = 1; i < values.size(); ++i)
        {
            std::cout << ", " << values[i];
        }
    }
    std::cout << "\"\n";

    return 0;
}

다수 : "1, 2, 3, 4, 5"


일반적으로 나는 반대 방식으로 수행합니다.

bool first=true;
for(const auto& item : items) {
    if(!first) cout<<separator;
    first = false;
    cout << item;
}

나는 단순한 제어 구조를 좋아합니다.

if (first == last) return;

while (true) {
  std::cout << *first;
  ++first;
  if (first == last) break;
  std::cout << separator;
}

취향에 따라 증분을 입력하고 한 줄로 테스트 할 수 있습니다.

...
while (true) {
  std::cout << *first;
  if (++first == last) break;
  std::cout << separator;
}

어딘가 에서 특별한 경우를 가지고 다닐 수는 없습니다 . 예를 들어 Boost의 String Algorithms Library 에는 조인 알고리즘이 있습니다. 구현 을 살펴보면 첫 번째 항목 (진행 구분 기호 없음)에 대한 특별한 경우를 볼 수 있고 다음 구분 기호가 추가됩니다.


두 개의 펑터를 인자로 취하는 취하는 함수 for_each_and_join 함수를 정의 할 수 있습니다. 첫 번째 펑 터는 각 요소와 함께 작동하고 두 번째 펑 터는 각 요소 요소 쌍과 함께 작동합니다.

#include <iostream>
#include <vector>

template <typename Iter, typename FEach, typename FJoin>
void for_each_and_join(Iter iter, Iter end, FEach&& feach, FJoin&& fjoin)
{
    if (iter == end)
        return;

    while (true) {
        feach(*iter);
        Iter curr = iter;
        if (++iter == end)
            return;
        fjoin(*curr, *iter);
    }
}

int main() {
    std::vector<int> values = { 1, 2, 3, 4, 5 };
    for_each_and_join(values.begin(), values.end()
    ,  [](auto v) { std::cout << v; }
    ,  [](auto, auto) { std::cout << ","; }
    );
}

라이브 예 : http://ideone.com/fR5S9H


int a[3] = {1,2,3};
int size = 3;
int i = 0;

do {
    std::cout << a[i];
} while (++i < size && std::cout << ", ");

다수 :

1, 2, 3 

목표는 &&평가 방식을 사용하는 것 입니다. 첫 번째 조건이 참이면 두 번째 조건을 평가합니다. 참이 아니면 두 번째 조건을 건너 뜁니다.


나는 "관용"에 대해 알고 있지만, C ++ 11 제공하지 않습니다 std::prevstd::next기능 양방향 반복자합니다.

int main() {
    vector<int> items = {0, 1, 2, 3, 4};
    string separator(",");

    // Guard to prevent possible segfault on prev(items.cend())
    if(items.size() > 0) {
        for(auto it = items.cbegin(); it != prev(items.cend()); it++) {
            cout << (*it) << separator;
        }
        cout << (*prev(items.cend()));
    }
}

나는 boost::join기능을 좋아한다 . 따라서보다 일반적인 동작의 경우 각 항목 쌍에 대해 호출되고 지속적인 상태를 가질 수있는 함수가 필요합니다. 람다를 사용하여 funcgion 호출로 사용합니다.

foreachpair (range, [](auto left, auto right){ whatever });

이제 범위 필터for 를 사용하여 일반 범위 기반 루프로 돌아갈 수 있습니다 !

for (auto pair : collection|aspairs) {
    Do-something_with (pair.first);
}

이 아이디어 pair에서 원래 컬렉션의 한 쌍의 인접 요소로 설정됩니다. "abcde"가 있으면 첫 번째 반복에서 first = 'a'및 second = 'b'가 제공됩니다. 다음에 first = 'b'및 second = 'c'를 통해; 기타

유사한 필터 접근 방식을 사용하여 / first / middle / last / 반복에 대한 열거로 각 반복 항목에 태그를 지정하는 튜플을 준비한 다음 루프 내에서 전환을 수행 할 수 있습니다.

마지막 요소를 단순히 생략하려면 마지막 요소를 제외하고 모두 범위 필터를 사용하십시오. 그것이 이미 Boost.Range에 있는지 또는 진행중인 Rangev3와 함께 제공 될 수 있는지는 모르겠지만, 이것이 일반 루프가 트릭을 수행하고 "단순"하게 만드는 일반적인 접근 방식입니다.


내가 사용하고 싶은 약간의 트릭은 다음과 같습니다.

양방향 반복 가능한 객체의 경우 : for ( auto it = items.begin(); it != items.end(); it++ ) { std::cout << *it << (it == items.end()-1 ? "" : sep); };

삼항 ?연산자를 사용하여 반복기의 현재 위치를 item.end()-1호출 과 비교합니다 . 반환 된 반복자 는 마지막 요소 item.end() 의 위치 참조하므로 실제 마지막 요소를 얻기 위해 한 번 감소시킵니다.

이 항목은 반복자의 마지막 요소가 아닌 경우, 우리는 (다른 곳에서 정의) 우리의 분리를 반환하거나 경우 입니다 마지막 요소는, 우리는 빈 문자열을 반환합니다.

단일 방향 반복 가능 (std :: forward_list로 테스트) : for ( auto it = items.begin(); it != items.end(); it++ ) { std::cout << *it << (std::distance( it, items.end() ) == 1 ? "" : sep); };

여기서는 이전 삼항 조건을 현재 반복기 위치와 반복기의 끝을 사용하여 std :: distance에 대한 호출로 대체합니다.

이 버전은 양방향 이터 러블과 단일 방향 이터 러블 모두에서 작동합니다.

편집 : 나는 실현 당신이 싫어하는 .begin().end()반복 입력,하지만 당신은 LOC가 카운트 다운 계속 찾고 있다면, 당신은 아마 피하다가이 경우를 기반으로 반복 범위에있는 것입니다.

"트릭"은 비교 논리가 비교적 간단한 경우 단일 삼항 표현식 내에서 비교 논리를 단순히 래핑하는 것입니다.

참고 URL : https://stackoverflow.com/questions/35372784/idioms-for-for-each-except-the-last-or-between-each-consecutive-pair-of-el

반응형