كيف يمكنني اكتشاف التكرار الأخير في حلقة فوق std::map؟

StackOverflow https://stackoverflow.com/questions/151046

  •  02-07-2019
  •  | 
  •  

سؤال

أحاول اكتشاف أفضل طريقة لتحديد ما إذا كنت في التكرار الأخير لحلقة فوق الخريطة من أجل القيام بشيء مثل ما يلي:

for (iter = someMap.begin(); iter != someMap.end(); ++iter) {
    bool last_iteration;
    // do something for all iterations
    if (!last_iteration) {
        // do something for all but the last iteration
    }
}

يبدو أن هناك عدة طرق للقيام بذلك:مكررات الوصول العشوائي، distance وظيفة، الخ.ما هي الطريقة الكنسية؟

يحرر:لا توجد مكررات وصول عشوائي للخرائط!

هل كانت مفيدة؟

المحلول

العنوان الأساسي؟لا أستطيع أن أدعي ذلك، ولكن أود أن أقترح

final_iter = someMap.end();
--final_iter;
if (iter != final_iter) ...

تم تحريره لتصحيح كما اقترح KTC. (شكرًا!في بعض الأحيان تتسرع وتخطئ في أبسط الأشياء...)

نصائح أخرى

منذ C++ 11، يمكنك أيضًا استخدام std::next()

   for (auto iter = someMap.begin(); iter != someMap.end(); ++iter) { 
        // do something for all iterations
        if (std::next(iter) != someMap.end()) {
            // do something for all but the last iteration
        }
    }

على الرغم من أن السؤال تم طرحه منذ فترة، إلا أنني أعتقد أنه سيكون من المفيد مشاركته.

يبدو أن هذا هو الأبسط:

bool last_iteration = iter == (--someMap.end());

إذا كنت تريد فقط استخدام ForwardIterator، فيجب أن يعمل هذا:

for ( i = c.begin(); i != c.end(); ) {
        iterator cur = i++;
        // do something, using cur
        if ( i != c.end() ) {
                // do something using cur for all but the last iteration
        }
}

تم تعديل Mark Ransom بحيث يعمل بالفعل على النحو المنشود.

finalIter = someMap.end();
--finalIter;
if (iter != final_iter)

من المفاجئ أن أحدًا لم يذكر ذلك بعد، ولكن بالطبع Boost لديه شيء ما ;)

دفعة.التالي (و ما يعادل Boost.Prior)

المثال الخاص بك سيبدو كما يلي:

for (iter = someMap.begin(); iter != someMap.end(); ++iter) {
    // do something for all iterations
    if (boost::next(iter) != someMap.end()) {
        // do something for all but the last iteration
    }
}

سيتم تحسين الكود التالي بواسطة مترجم بحيث يكون الحل الأفضل لهذه المهمة من خلال الأداء وكذلك من خلال قواعد OOP:

if (&*it == &*someMap.rbegin()) {
    //the last iteration
}

هذا هو أفضل رمز وفقًا لقواعد OOP لأن std::map يحتوي على وظيفة عضو خاصة rbegin للكود مثل:

final_iter = someMap.end();
--final_iter;
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <algorithm>

using namespace boost::lambda;

// call the function foo on each element but the last...
if( !someMap.empty() )
{
  std::for_each( someMap.begin(), --someMap.end(), bind( &Foo, _1 ) );
}

باستخدام std::for_each سيضمن أن الحلقة ضيقة ودقيقة...لاحظ مقدمة الدالة foo() التي تأخذ وسيطة واحدة (يجب أن يتطابق النوع مع ما هو موجود في someMap).يحتوي هذا النهج على إضافة إضافية تتمثل في كونه سطرًا واحدًا.بالطبع، إذا كان Foo صغيرًا حقًا، فيمكنك استخدام دالة lambda والتخلص من استدعاء &Foo.

لماذا تعمل على العثور على EOF حتى لا تعطي شيئًا لها.

ببساطة، استبعده؛

for (iter = someMap.begin(); someMap.end() - 1; ++iter) {
    //apply to all from begin to second last element
}

قبلة (اجعل الأمر بسيطًا)

طريقة بسيطة ولكنها فعالة:

  size_t items_remaining = someMap.size();

  for (iter = someMap.begin(); iter != someMap.end(); iter++) {
    bool last_iteration = items_remaining-- == 1;
  }

إليكم رأيي الأمثل:

iter = someMap.begin();

do {
    // Note that curr = iter++ may involve up to three copy operations
    curr = iter;

    // Do stuff with curr

    if (++iter == someMap.end()) {
        // Oh, this was the last iteration
        break;
    }

    // Do more stuff with curr

} while (true);

ماذا عن هذا، لم يذكر أحد ولكن...

for (iter = someMap.begin(); iter != someMap.end(); ++iter) {
    // do something for all iterations
    if (iter != --someMap.end()) {
        // do something for all but the last iteration
    }
}

هذا يبدو بسيطا، مم ...

البرنامج الكامل:

#include <iostream>
#include <list>

void process(int ii)
{
   std::cout << " " << ii;
}

int main(void)
{
   std::list<int> ll;

   ll.push_back(1);
   ll.push_back(2);
   ll.push_back(3);
   ll.push_back(4);
   ll.push_back(5);
   ll.push_back(6);

   std::list<int>::iterator iter = ll.begin();
   if (iter != ll.end())
   {
      std::list<int>::iterator lastIter = iter;
      ++ iter;
      while (iter != ll.end())
      {
         process(*lastIter);
         lastIter = iter;
         ++ iter;
      }
      // todo: think if you need to process *lastIter
      std::cout << " | last:";
      process(*lastIter);
   }

   std::cout << std::endl;

   return 0;
}

ينتج هذا البرنامج:

 1 2 3 4 5 | last: 6

يمكنك فقط سحب عنصر من الخريطة قبل التكرار، ثم تنفيذ عمل "التكرار الأخير" خارج الحلقة ثم إعادة العنصر مرة أخرى داخل الخريطة.يعد هذا أمرًا سيئًا للغاية بالنسبة للتعليمات البرمجية غير المتزامنة، ولكن بالنظر إلى مدى سوء بقية لغة C++ بالنسبة للتزامن، لا أعتقد أنها ستكون مشكلة.:-)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top