এর মাধ্যমে পুনরাবৃত্তি করার সময় আপনি কী স্ট্যান্ড :: তালিকা থেকে উপাদানগুলি সরাতে পারবেন?


239

আমি দেখতে এমন কোড পেয়েছি যা দেখতে দেখতে:

for (std::list<item*>::iterator i=items.begin();i!=items.end();i++)
{
    bool isActive = (*i)->update();
    //if (!isActive) 
    //  items.remove(*i); 
    //else
       other_code_involving(*i);
}
items.remove_if(CheckItemNotActive);

আমি আবার নিষ্ক্রিয় আইটেমগুলিকে আপডেট করার সাথে সাথেই মুছে ফেলাতে চাই, আবার তালিকার পদচারণা এড়ানোর জন্য order তবে যদি আমি মন্তব্য-আউট লাইনগুলি যোগ করি তবে আমি একটি ত্রুটি পেয়ে যাব i++: "তালিকার পুনরাবৃত্তিকে ইনক্রিমেন্টেবল নয়"। আমি এমন কিছু বিকল্প চেষ্টা করেছিলাম যা বিবৃতিতে বর্ধিত হয় নি, তবে আমি কাজ করার মতো কিছুই পাই না।

স্টাডি :: তালিকার সাথে হাঁটতে হাঁটতে আইটেমগুলি সরিয়ে দেওয়ার সর্বোত্তম উপায় কী?


আমি পিছনে পিছনে পুনরাবৃত্তির উপর ভিত্তি করে কোনও সমাধান দেখিনি। আমি পোস্ট এক ধরনের
সানচো.স পুনরায় ইনস্টল করুনমনিকাসেলিও

উত্তর:


286

আপনাকে প্রথমে পুনরুক্তি বাড়িয়ে তুলতে হবে (i ++ সহ) এবং তারপরে পূর্ববর্তী উপাদানটি (যেমন, i ++ থেকে ফেরত মান ব্যবহার করে) সরিয়ে ফেলতে হবে। আপনি কোডটি কিছুক্ষণ এর মতো লুপে পরিবর্তন করতে পারেন:

std::list<item*>::iterator i = items.begin();
while (i != items.end())
{
    bool isActive = (*i)->update();
    if (!isActive)
    {
        items.erase(i++);  // alternatively, i = items.erase(i);
    }
    else
    {
        other_code_involving(*i);
        ++i;
    }
}

7
আসলে, এটি কাজের নিশ্চয়তা দেয় না। "মুছে ফেলুন (i ++);" দিয়ে আমরা কেবল জানি যে প্রাক-বর্ধিত মান মুছে ফেলা () মুছে ফেলা হয়েছে, এবং আমি আধা-কোলনের আগে বাড়িয়ে দেওয়া হয়েছে, কলটি মুছে ফেলার আগেই প্রয়োজন হয় না ()। "পুনরাবৃত্তকারী prev = i ++; মুছুন (পূর্ব);" রিটার্ন মানটি যেমন ব্যবহার করা যায় তেমন কাজ করার ব্যাপারে নিশ্চিত
জেমস কারান

58
না জেমস, মুছে ফেলার আগে আমি বর্ধিত হয় এবং পূর্ববর্তী মানটি ফাংশনে প্রেরণ করা হয়। ফাংশনটি বলার আগে কোনও ফাংশনের যুক্তিগুলির সম্পূর্ণ মূল্যায়ন করতে হবে।
ব্রায়ান নিল

28
@ জেমস কারান: এটি সঠিক নয়। কোনও ফাংশন বলার আগে সমস্ত যুক্তি পুরোপুরি মূল্যায়ন করা হয়।
মার্টিন ইয়র্ক

9
মার্টিন ইয়র্ক ঠিক আছে। কোনও ফাংশন বলার আগে কোনও ফাংশন কলের সমস্ত যুক্তি পুরোপুরি মূল্যায়ন করা হয়, কোনও ব্যতিক্রম নেই। এটি কেবল ফাংশন কীভাবে কাজ করে। আপনার foo.b (i ++) সি এর সাথে (আই ++) উদাহরণ নেই (যা কোনও ক্ষেত্রেই
সংজ্ঞায়িত

75
বিকল্প ব্যবহারটি i = items.erase(i)নিরাপদ, কারণ এটি কোনও তালিকার সমতুল্য, তবে কেউ যদি ধারকটিকে ভেক্টরে পরিবর্তন করেন তবে এখনও কাজ করবে। একটি ভেক্টর দিয়ে মুছে ফেলুন () সমস্ত কিছু বাম দিকে সরাতে গর্তটি পূরণ করতে। যদি আপনি কোডটি দিয়ে শেষ আইটেমটি মুছে ফেলার চেষ্টা করেন যা মুছে যাওয়ার পরে পুনরুক্তি বৃদ্ধি করে, প্রান্তটি বাম দিকে চলে যায় এবং পুনরুক্তিটি ডান দিকে চলে যায় - শেষের অতীতকে । এবং তারপর আপনি ক্রাশ।
এরিক সেপ্পেনেন

133

তুমি করতে চাও:

i= items.erase(i);

এটি আপনার পুনরাবৃত্তি সরানোর পরে পুনরায় অবস্থানকারীর সঠিক অবস্থান আপডেট করবে।


80
সতর্কতা অবলম্বন করুন যে আপনি সেই কোডটি আপনার লুপের মধ্যে ফেলে দিতে পারবেন না। অন্যথায় আপনি যখনই কোনও উপাদান সরিয়ে ফেলবেন তখনই আপনি কোনও উপাদান এড়িয়ে যাবেন।
মাইকেল ক্রিস্টফিক

2
তিনি কি করতে পারবেন না -; প্রতিবার এড়িয়ে যাওয়া এড়ানোর জন্য তার কোডের অংশটি অনুসরণ করছেন?
উত্সাহী

1
@ অেন্থুসিস্টিকগেক, যদি হয় তবে i==items.begin()?
এমএসএএন

1
@ এনথুসিয়াস্টিকগেক, এই মুহুর্তে আপনার ঠিক করা উচিত i= items.erase(i);। এটি আধ্যাত্মিক ফর্ম এবং ইতিমধ্যে এই সমস্ত বিবরণ যত্ন নেয়।
এমএসএন

6
মাইকেল একটি বিশাল 'গোটচা' দেখায়, আমাকে এখনই একই জিনিসটি মোকাবেলা করতে হয়েছিল। আমি এটিকে এড়িয়ে যাওয়ার সহজ পদ্ধতিটি হ'ল কিছুক্ষণের জন্য () লুপটি ক্ষয় করা এবং বর্ধিতকরণ সম্পর্কে সতর্কতা অবলম্বন করা হয়েছিল
আন কুইন

22

আপনাকে ক্রিস্টোর উত্তর এবং এমএসএন এর সংমিশ্রণটি করতে হবে:

// Note: Using the pre-increment operator is preferred for iterators because
//       there can be a performance gain.
//
// Note: As long as you are iterating from beginning to end, without inserting
//       along the way you can safely save end once; otherwise get it at the
//       top of each loop.

std::list< item * >::iterator iter = items.begin();
std::list< item * >::iterator end  = items.end();

while (iter != end)
{
    item * pItem = *iter;

    if (pItem->update() == true)
    {
        other_code_involving(pItem);
        ++iter;
    }
    else
    {
        // BTW, who is deleting pItem, a.k.a. (*iter)?
        iter = items.erase(iter);
    }
}

অবশ্যই, সবচেয়ে দক্ষ এবং সুপারকুল® এসটিএল স্যাভি জিনিসটি এরকম কিছু হবে:

// This implementation of update executes other_code_involving(Item *) if
// this instance needs updating.
//
// This method returns true if this still needs future updates.
//
bool Item::update(void)
{
    if (m_needsUpdates == true)
    {
        m_needsUpdates = other_code_involving(this);
    }

    return (m_needsUpdates);
}

// This call does everything the previous loop did!!! (Including the fact
// that it isn't deleting the items that are erased!)
items.remove_if(std::not1(std::mem_fun(&Item::update)));

আমি আপনার সুপারকুল পদ্ধতিটি বিবেচনা করেছি, আমার দ্বিধা ছিল যে সরানোর_আইফের কলটি এটি পরিষ্কার করে দেয় না যে লক্ষ্যগুলি আইটেমগুলি সক্রিয়করণের তালিকা থেকে সরিয়ে না দিয়ে প্রক্রিয়া করা to (আইটেমগুলি মুছে ফেলা হচ্ছে না কারণ এগুলি কেবল নিষ্ক্রিয় হয়ে পড়েছে,
বিনা বাঁধা

আমার ধারণা আপনি ঠিক আছেন অস্পষ্টতা দূর করতে আমি একদিকে 'আপডেট' নাম পরিবর্তন করার পরামর্শ দিতে চাইছি, তবে সত্য কথাটি, এই কোডটি ফান্ট্যাক্টরদের সাথে অভিনব, তবে এটি অস্পষ্ট ছাড়াও কিছু নয়।
মাইকে

সুষ্ঠু মন্তব্য, হয় শেষ ব্যবহার করতে সময় লুপ ঠিক করুন বা অব্যবহৃত সংজ্ঞাটি মুছুন।
মাইক 21

10

অ্যালগরিদম স্ট্যান্ড :: অপসারণ_টি ব্যবহার করুন।

সম্পাদনা করুন: সংগ্রহগুলির সাথে কাজটি এমন হওয়া উচিত: 1. সংগ্রহ প্রস্তুত করুন। 2. প্রক্রিয়া সংগ্রহ।

আপনি এই পদক্ষেপগুলি মিশ্রিত না করলে জীবন আরও সহজ হবে।

  1. এসটিডি :: remove_if। বা তালিকা :: অপসারণ_আইফ (যদি আপনি জানেন যে আপনি তালিকার সাথে কাজ করছেন এবং টি-কালেকশনের সাথে নয়)
  2. এসটিডি :: for_each

2
স্টাড :: তালিকার একটি মুছে ফেলা_আইফ সদস্য ফাংশন রয়েছে যা অপসারণ_এফ অ্যালগরিদমের তুলনায় আরও কার্যকর (এবং "মুছে ফেলার" আইডিয়ামের প্রয়োজন হয় না)।
ব্রায়ান নিল

5

এখানে এমন একটি forলুপ ব্যবহার করে একটি তালিকা যা তালিকার পুনরাবৃত্তি করে এবং তালিকাটি ক্রমবর্ধনের সময় কোনও আইটেম অপসারণের ঘটনায় পুনরাবৃত্তিকে পুনরায়করণ করে বা পুনরাবৃত্তি করে।

for(auto i = items.begin(); i != items.end();)
{
    if(bool isActive = (*i)->update())
    {
        other_code_involving(*i);
        ++i;

    }
    else
    {
        i = items.erase(i);

    }

}

items.remove_if(CheckItemNotActive);

4

ক্রিস্টোর উত্তরের লুপ সংস্করণের বিকল্প।

আপনি কিছু দক্ষতা হারাবেন, আপনি মোছার সময় পিছনে যান এবং তারপরে আবার এগিয়ে যান তবে অতিরিক্ত পুনরাবৃত্তির বর্ধনের বিনিময়ে আপনি লুপের স্কোপে এবং কোডটি কিছুটা পরিষ্কার পরিচ্ছন্ন দেখায় পুনরুক্তি করতে পারেন। কী নির্বাচন করবেন তা মুহুর্তের অগ্রাধিকারের উপর নির্ভর করে।

উত্তরটি সম্পূর্ণ সময়ের বাইরে ছিল, আমি জানি ...

typedef std::list<item*>::iterator item_iterator;

for(item_iterator i = items.begin(); i != items.end(); ++i)
{
    bool isActive = (*i)->update();

    if (!isActive)
    {
        items.erase(i--); 
    }
    else
    {
        other_code_involving(*i);
    }
}

1
এটি আমিও ব্যবহার করেছি। তবে কনটেনারের প্রথম উপাদানটি অপসারণ করা উচিত যদি এটির কাজ করার গ্যারান্টিযুক্ত আমি নিশ্চিত নই। আমার জন্য, এটি কাজ করে, আমি মনে করি, তবে আমি নিশ্চিত নই যে এটি প্ল্যাটফর্ম জুড়ে বহনযোগ্য।
ট্রোলো

আমি "-1" করিনি, তবে তালিকার পুনরুক্তি হ্রাস করতে পারে না? কমপক্ষে আমি ভিজ্যুয়াল স্টুডিও ২০০৮ থেকে
জোর

লিঙ্কযুক্ত তালিকাটি যতক্ষণ না একটি মাথা / স্টাব নোড (শেষ () হিসাবে ব্যবহৃত হয়) এবং রিবেইন () হিসাবে খালি ব্যবহার করা হয় এবং এটি (রেন্ড () হিসাবে খালি ব্যবহার করা হয়) হিসাবে একটি বিজ্ঞপ্তি ডাবল লিঙ্কযুক্ত তালিকা হিসাবে প্রয়োগ করা হয় এটি কাজ করবে। আমি কোন প্ল্যাটফর্মটিতে এটি ব্যবহার করছিলাম তা আমি মনে করতে পারি না তবে এটি আমার পক্ষেও কাজ করে যাচ্ছিল, কারণ উপরে বর্ণিত প্রয়োগটি একটি স্ট্যান্ড :: তালিকার সর্বাধিক সাধারণ বাস্তবায়ন। তবে যাইহোক, এটি প্রায় নিশ্চিত যে এটি কিছু অপরিজ্ঞাত (সি ++ স্ট্যান্ডার্ড দ্বারা) আচরণ ব্যবহার করছিল, সুতরাং এটি আরও ভাল ব্যবহার করবেন না।
রাফায়েল গাগো

Re: পদ্ধতি প্রয়োজন । কিছু সংগ্রহের বাস্তবায়ন এমন একটি সরবরাহ করে যা দৃ causes়তার কারণ হয়। iterator cannot be decrementederaserandom access iteratorforward only iterator
জেসি চিশল্ম

@ জেসি চিশল্ম প্রশ্নটি একটি স্ট্যান্ড :: তালিকা সম্পর্কে ছিল, কোনও স্বেচ্ছাচারী ধারক নয়। std :: তালিকা মুছে ফেলা এবং দ্বিদ্বিদী পুনরুক্তি সরবরাহ করে।
রাফায়েল গাগো

4

আমার এটি সংক্ষেপে রয়েছে, উদাহরণ সহ তিনটি পদ্ধতি এখানে রয়েছে:

1. whileলুপ ব্যবহার করে

list<int> lst{4, 1, 2, 3, 5};

auto it = lst.begin();
while (it != lst.end()){
    if((*it % 2) == 1){
        it = lst.erase(it);// erase and go to next
    } else{
        ++it;  // go to next
    }
}

for(auto it:lst)cout<<it<<" ";
cout<<endl;  //4 2

2. remove_ifতালিকায় সদস্য ফান্ট ব্যবহার :

list<int> lst{4, 1, 2, 3, 5};

lst.remove_if([](int a){return a % 2 == 1;});

for(auto it:lst)cout<<it<<" ";
cout<<endl;  //4 2

৩. সদস্য ফাংশনের std::remove_ifসাথে সম্মিলিত ফান্ট ব্যবহার করা erase:

list<int> lst{4, 1, 2, 3, 5};

lst.erase(std::remove_if(lst.begin(), lst.end(), [](int a){
    return a % 2 == 1;
}), lst.end());

for(auto it:lst)cout<<it<<" ";
cout<<endl;  //4 2

4. forলুপ ব্যবহার করে, পুনরাবৃত্তির আপডেটটি নোট করা উচিত:

list<int> lst{4, 1, 2, 3, 5};

for(auto it = lst.begin(); it != lst.end();++it){
    if ((*it % 2) == 1){
        it = lst.erase(it);  erase and go to next(erase will return the next iterator)
        --it;  // as it will be add again in for, so we go back one step
    }
}

for(auto it:lst)cout<<it<<" ";
cout<<endl;  //4 2 

2

অপসারণ কেবল পুনরাবৃত্তিকে অবৈধ করে যা সরানো উপাদানগুলিকে নির্দেশ করে।

সুতরাং এই ক্ষেত্রে * আমি সরানোর পরে, আমি অবৈধ এবং আপনি এটিতে বৃদ্ধি করতে পারবেন না।

আপনি যা করতে পারেন তা হ'ল সরকারী উপাদানটির পুনরাবৃত্তিকে সংরক্ষণ করুন, তারপরে পুনরাবৃত্তিকে বৃদ্ধি করুন এবং তারপরে সংরক্ষিতটিকে অপসারণ করুন।


2
পোস্ট-ইনক্রিমেন্ট ব্যবহার করা অনেক বেশি মার্জিত।
ব্রায়ান নিল

2

যদি আপনি std::listএকটি কাতারের মতোই মনে করেন তবে আপনি যে আইটেমগুলি রাখতে চান তা সনাক্ত এবং প্রেরণ করতে পারেন তবে আপনি যে আইটেমটি সরাতে চান তা কেবল সন্ধান করুন (এবং সজ্জা নয়)। এখানে একটি উদাহরণ রয়েছে যেখানে আমি 1-10 নম্বরযুক্ত তালিকা থেকে 5 সরিয়ে নিতে চাই ...

std::list<int> myList;

int size = myList.size(); // The size needs to be saved to iterate through the whole thing

for (int i = 0; i < size; ++i)
{
    int val = myList.back()
    myList.pop_back() // dequeue
    if (val != 5)
    {
         myList.push_front(val) // enqueue if not 5
    }
}

myList এখন কেবল সংখ্যা 1-4 এবং 6-10 হবে।


আকর্ষণীয় পদ্ধতির তবে আমি ভয় করছি যে এটি যদিও ধীর হতে পারে।
স্যাজি

2

পিছন দিকে ইটারেটিং করা উপাদানগুলিকে অনুসরণ করার জন্য কোনও উপাদান মুছে ফেলার প্রভাব এড়িয়ে চলে:

typedef list<item*> list_t;
for ( list_t::iterator it = items.end() ; it != items.begin() ; ) {
    --it;
    bool remove = <determine whether to remove>
    if ( remove ) {
        items.erase( it );
    }
}

দ্রষ্টব্য: দেখুন এই অনগ্রসর পুনরাবৃত্তির সংক্রান্ত, যেমন,।

পিএস 2: আমি যদি ভালভাবে শেষ করার উপাদানগুলি ভালভাবে পরিচালনা করি তবে আমি পুরোপুরি পরীক্ষা করে দেখিনি।


আবার: avoids the effect of erasing an element on the remaining elementsএকটি তালিকার জন্য, সম্ভবত হ্যাঁ। কোনও ভেক্টরের জন্য নাও হতে পারে। এটি নির্বিচারে সংগ্রহের গ্যারান্টিযুক্ত কিছু নয়। উদাহরণস্বরূপ, একটি মানচিত্র পারে নিজেই rebalance করার সিদ্ধান্ত নেন।
জেসি চিশল্ম

1

তুমি লিখতে পারো

std::list<item*>::iterator i = items.begin();
while (i != items.end())
{
    bool isActive = (*i)->update();
    if (!isActive) {
        i = items.erase(i); 
    } else {
        other_code_involving(*i);
        i++;
    }
}

আপনি সমতুল্য কোড সহ লিখতে পারেন std::list::remove_if যা কম ভার্বোস এবং আরও স্পষ্ট

items.remove_if([] (item*i) {
    bool isActive = (*i)->update();
    if (!isActive) 
        return true;

    other_code_involving(*i);
    return false;
});

std::vector::erase std::remove_ifবাগ্ধারা যখন আইটেম একটি তালিকা পরিবর্তে একটি ভেক্টর হে (ঢ) এ compexity রাখা ব্যবহার করা উচিত - অথবা যদি আপনি জেনেরিক কোড লিখতে এবং আইটেম (একটি ভেক্টর মত) একক আইটেম নিশ্চিহ্ন কোন কার্যকরী পথ সঙ্গে একটি ধারক হতে পারে

items.erase(std::remove_if(begin(items), end(items), [] (item*i) {
    bool isActive = (*i)->update();
    if (!isActive) 
        return true;

    other_code_involving(*i);
    return false;
}));

-4

আমার মনে হয় আপনার এখানে একটি বাগ আছে, আমি এইভাবে কোড করি:

for (std::list<CAudioChannel *>::iterator itAudioChannel = audioChannels.begin();
             itAudioChannel != audioChannels.end(); )
{
    CAudioChannel *audioChannel = *itAudioChannel;
    std::list<CAudioChannel *>::iterator itCurrentAudioChannel = itAudioChannel;
    itAudioChannel++;

    if (audioChannel->destroyMe)
    {
        audioChannels.erase(itCurrentAudioChannel);
        delete audioChannel;
        continue;
    }
    audioChannel->Mix(outBuffer, numSamples);
}

আমি অনুমান করছি এটি শৈলীর পছন্দগুলির জন্য নিম্নমানের ছিল, কারণ এটি কার্যকরী বলে মনে হয়। হ্যাঁ, অবশ্যই, (1) এটি একটি অতিরিক্ত পুনরুক্তি ব্যবহার করে, (২) পুনরুক্তি বৃদ্ধিটি কোনও লুপের জন্য সেখানে রাখার কোনও ভাল কারণ ছাড়াই একটি অদ্ভুত স্থানে রয়েছে, (3) এটি মুছে ফেলার সিদ্ধান্তের পরে চ্যানেলটি কাজ করে ওপিতে যেমন পছন্দ তবে এটি কোনও ভুল উত্তর নয়।
জেসি চিশল্ম
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.