मैं C ++ के साथ उनके अंदर "अगर" स्थिति के साथ "छोरों" के लिए कैसे बच सकता हूं?


111

लगभग सभी कोड जो मैं लिखता हूं, मैं अक्सर संग्रह पर समस्याओं को कम करने के साथ काम कर रहा हूं जो अंततः "अगर उनके अंदर की स्थिति" के साथ अनुभवहीन हैं। यहाँ एक सरल उदाहरण है:

for(int i=0; i<myCollection.size(); i++)
{
     if (myCollection[i] == SOMETHING)
     {
           DoStuff();
     }
}

कार्यात्मक भाषाओं के साथ, मैं संग्रह को दूसरे संग्रह (आसानी से) को कम करके समस्या को हल कर सकता हूं और फिर मेरे कम किए गए सेट पर सभी ऑपरेशन कर सकता हूं। छद्मकोड में:

newCollection <- myCollection where <x=true
map DoStuff newCollection

और दूसरे C वेरिएंट में, C # की तरह, मैं एक क्लॉज जैसे कम कर सकता हूं

foreach (var x in myCollection.Where(c=> c == SOMETHING)) 
{
   DoStuff();
}

या बेहतर (कम से कम मेरी आँखों के लिए)

myCollection.Where(c=>c == Something).ToList().ForEach(d=> DoStuff(d));

बेशक, मैं बहुत कुछ मिश्रण और व्यक्तिपरक / राय आधारित शैली कर रहा हूं, लेकिन मैं मदद नहीं कर सकता लेकिन मुझे लगता है कि मैं वास्तव में मौलिक कुछ याद कर रहा हूं जो मुझे सी ++ के साथ इस पसंदीदा तकनीक का उपयोग करने की अनुमति दे सकता है। क्या कोई मुझे बता सकता है?


7
C ++ मानक लाइब्रेरी कार्यक्षमता में से, आप कोशिश कर सकते हैं std::copy_if, लेकिन चयन आलसी नहीं हैं
मिलेनियमबग

14
आपको रेंज-वी 3 में रुचि हो सकती है । यह टीएस के रूप में सी ++ में भी आना चाहिए और भविष्य में रिलीज होने की उम्मीद है।
नाथनऑलिवर

12
मुझे यह इंगित करने की आवश्यकता है कि आपके द्वारा उल्लेख किया गया ifअंदर forन केवल अन्य उदाहरणों के बराबर बहुत अधिक कार्यात्मक है, बल्कि बहुत सारे मामलों में तेज भी होगा। कार्यात्मक शैली पसंद करने का दावा करने वाले किसी व्यक्ति के लिए, आप जिस चीज़ को बढ़ावा दे रहे हैं, वह कार्यात्मक प्रोग्रामिंग की पवित्रता की प्रिय अवधारणा के खिलाफ जाना प्रतीत होता है क्योंकि DoStuffस्पष्ट रूप से साइड इफेक्ट्स हैं।
फराप

60
मुझे कभी भी समझ नहीं आया कि लोग एक ही लाइन पर सभी लॉजिक को एक साथ जोड़कर इसे किसी भी तरह से बेहतर या अधिक पठनीय क्यों बनाते हैं। आपका C ++ स्निपेट सबसे ऊपर है जो अब तक आपकी सभी संभावनाओं में से सबसे अधिक पठनीय है। और जब से दक्षता नहीं बदली जाएगी, मैं नहीं समझ सकता कि आप ऐसा क्यों नहीं लिखना पसंद करेंगे, जब तक कि आपके द्वारा हटाए गए कोड की पंक्तियों की संख्या से आपको भुगतान नहीं किया जाता है।
कोड़ी ग्रे

10
@ कोडीग्रे एग्री: यह सिंटैक्टिक शुगर है। और प्रश्न शीर्षक भ्रामक है, क्योंकि यह बहुत अलग है कि यह ब्रांचिंग से बच जाता है और इसे अमूर्तन के तहत छुपाता है।
edmz

जवाबों:


99

IMHO यह अधिक सीधा आगे है और इसके अंदर अगर एक के साथ लूप के लिए उपयोग करने के लिए अधिक पठनीय है। हालांकि, अगर यह आपके लिए कष्टप्रद है, तो आप for_each_ifनीचे दिए गए एक का उपयोग कर सकते हैं :

template<typename Iter, typename Pred, typename Op> 
void for_each_if(Iter first, Iter last, Pred p, Op op) {
  while(first != last) {
    if (p(*first)) op(*first);
    ++first;
  }
}

उदाहरण:

std::vector<int> v {10, 2, 10, 3};
for_each_if(v.begin(), v.end(), [](int i){ return i > 5; }, [](int &i){ ++i; });

लाइव डेमो


10
यह असाधारण रूप से चतुर है। मैं यह भी स्वीकार करूंगा कि यह सीधे आगे नहीं है और मैं शायद तभी उपयोग करूंगा जब C ++ की प्रोग्रामिंग की जाएगी, जब दूसरों द्वारा उपभोग की जाने वाली प्रोग्रामिंग। लेकिन यह वही है जो मुझे अपने निजी उपयोग के लिए चाहिए! :)
डार्कनर

14
@Default कंटेनरों के बजाय इटरेटर जोड़े को पास करना अधिक लचीला और मुहावरेदार C ++ दोनों है।
मार्क बी

8
@ स्लाव, सामान्य श्रेणियों में एल्गोरिदम की संख्या कम नहीं होगी। उदाहरण के लिए, आपको अभी भी जरूरत है find_ifऔर findचाहे वे रेंजर्स या जोड़े के पुनरावृत्तियों पर काम करें। (कुछ अपवाद हैं, जैसे कि for_eachऔरfor_each_n )। हर छींक के लिए नए अल्गोस लिखने से बचने का तरीका मौजूदा एल्गो के साथ अलग-अलग ऑपरेशन का उपयोग करना है, जैसे कि कॉल for_each_ifकरने योग्य में शर्त को एम्बेड करने के बजाय for_each, जैसेfor_each(first, last, [&](auto& x) { if (cond(x)) f(x); });
जोनाथन वेकली

9
मैं पहला वाक्य के साथ सहमत होना होगा जा रहा हूँ: के लिए-अगर हल है मानक बहुत अधिक पठनीय और के साथ काम करने के लिए आसान। मुझे लगता है कि लंबोदर सिंटैक्स और एक साधारण लूप को संभालने के लिए कहीं और परिभाषित टेम्पलेट का उपयोग चिड़चिड़ा या संभवतः अन्य देवताओं को भ्रमित करेगा। आप स्थानीयता और प्रदर्शन के लिए त्याग कर रहे हैं ... क्या? एक पंक्ति में कुछ लिखने में सक्षम होने के नाते?
user1354557

45
खांसी @Darkenor, आम तौर पर " असाधारण रूप से चतुर" प्रोग्रामिंग से बचा जाना है क्योंकि यह आपके भविष्य के स्वयं सहित बाकी सभी लोगों के लिए बकवास की घोषणा करता है
रयान

48

बूस्ट उन श्रेणियों को प्रदान करता है जिनका उपयोग w / range-based के लिए किया जा सकता है। रेंजों का यह फायदा है कि वे अंतर्निहित डेटा संरचना की नकल नहीं करते हैं, वे केवल एक 'दृश्य' प्रदान करते हैं (अर्थात begin(),end() श्रृंखला के लिए और operator++(), operator==()इटरेटर के लिए)। यह आपकी रुचि का हो सकता है: http://www.boost.org/libs/range/doc/html/range/reference/adaptors/reference/filtered.html

#include <boost/range/adaptor/filtered.hpp>
#include <iostream>
#include <vector>

struct is_even
{
    bool operator()( int x ) const { return x % 2 == 0; }
};

int main(int argc, const char* argv[])
{
    using namespace boost::adaptors;

    std::vector<int> myCollection{1,2,3,4,5,6,7,8,9};

    for( int i: myCollection | filtered( is_even() ) )
    {
        std::cout << i;
    }
}

1
क्या मैं इसके बजाय OP उदाहरण का उपयोग करने का सुझाव दे सकता हूं, अर्थात is_even=> condition, input=> myCollectionआदि
डिफ़ॉल्ट

यह एक बहुत अच्छा जवाब है और निश्चित रूप से मैं जो करना चाहता हूं। मैं स्वीकार करने पर रोक लगाने जा रहा हूं जब तक कि कोई ऐसा करने के लिए मानक अनुपालन के साथ नहीं आ सकता है जो आलसी / स्थगित निष्पादन का उपयोग करता है। Upvoted।
डार्कनर

5
@Darkenor: यदि बूस्ट आपके लिए एक समस्या है (उदाहरण के लिए, आपको कंपनी की नीति और प्रबंधक ज्ञान के कारण इसका उपयोग करने पर प्रतिबंध लगा दिया गया है), तो मैं filtered()आपके लिए एक सरलीकृत परिभाषा के साथ आ सकता हूं - उन्होंने कहा, इसका उपयोग करना बेहतर है कुछ तदर्थ कोड की तुलना में एक समर्थित परिवाद।
लोरो

आपसे पूर्णतः सहमत हूँ। मैंने इसे स्वीकार कर लिया क्योंकि मानक-अनुरूप तरीका जो पहले आया था क्योंकि प्रश्न C ++ में ही था, न कि बूस्ट लाइब्रेरी। लेकिन यह वास्तव में उत्कृष्ट है। इसके अलावा - हाँ, मैंने दुख की बात है कि कई जगहों पर काम किया है, जो बेहूदा कारणों से बूस्ट पर प्रतिबंध
लगाता है

@LeeClagett:? ।
लॉर्रो

44

एक नया एल्गोरिथ्म बनाने के बजाय, जैसा कि स्वीकृत उत्तर करता है, आप एक मौजूदा फ़ंक्शन का उपयोग कर सकते हैं जो शर्त लागू करता है:

std::for_each(first, last, [](auto&& x){ if (cond(x)) { ... } });

या यदि आप वास्तव में एक नया एल्गोरिथ्म चाहते हैं, तो कम से कम पुन: उपयोग की for_eachजगह पुनरावृत्ति तर्क की नकल करें:

template<typename Iter, typename Pred, typename Op> 
  void
  for_each_if(Iter first, Iter last, Pred p, Op op) {
    std::for_each(first, last, [&](auto& x) { if (p(x)) op(x); });
  }

मानक पुस्तकालय का उपयोग करने के लिए बहुत बेहतर और स्पष्ट है।
अनाम

4
क्योंकि std::for-each(first, last, [&](auto& x) {if (p(x)) op(x); });पूरी तरह से आसान है for (Iter x = first; x != last; x++) if (p(x)) op(x);}?
user253751

2
@ पुस्तकालय का पुन: उपयोग करने से मानक पुस्तकालय के अन्य लाभ होते हैं, जैसे कि इटरेटर की वैधता की जाँच, या (C ++ 17 में) समांतर करना अधिक आसान है, बस एक और तर्क जोड़कर: std::for_each(std::execution::par, first, last, ...);उन चीजों को हस्तलिखित पाश में जोड़ना कितना आसान है?
जोनाथन वेकली

1
#pragma omp के लिए समानांतर
मार्क के कोवन

2
@ क्षमा करें, आपके स्रोत कोड या बिल्ड चेन के कुछ रैंडम क्विक ने बनाया है कि बिना किसी डायग्नोस्टिक के साथ झुंझलाहट से समानान्तर गैर-मानक संकलक विस्तार शून्य प्रदर्शन को बढ़ावा देता है।
यक्क - एडम नेवरामोंट

21

टालने का विचार

for(...)
    if(...)

एक एंटीपैटर्न के रूप में निर्माण बहुत व्यापक है।

यह कई आइटमों को संसाधित करने के लिए पूरी तरह से ठीक है जो एक लूप के अंदर से एक निश्चित अभिव्यक्ति से मेल खाते हैं, और कोड उससे अधिक स्पष्ट नहीं हो सकता है। यदि प्रसंस्करण स्क्रीन पर फिट होने के लिए बहुत बड़ा हो जाता है, तो यह सबरूटीन का उपयोग करने का एक अच्छा कारण है, लेकिन फिर भी सशर्त को लूप के अंदर रखा जाता है, अर्थात

for(...)
    if(...)
        do_process(...);

के लिए काफी बेहतर है

for(...)
    maybe_process(...);

यह एक एंटीपार्टर्न बन जाता है जब केवल एक तत्व का मिलान होगा, क्योंकि तब यह तत्व की पहली खोज करने के लिए स्पष्ट होगा, और लूप के बाहर प्रसंस्करण का प्रदर्शन करेगा।

for(int i = 0; i < size; ++i)
    if(i == 5)

इसका एक चरम और स्पष्ट उदाहरण है। अधिक सूक्ष्म, और इस तरह अधिक सामान्य, जैसे एक कारखाना पैटर्न है

for(creator &c : creators)
    if(c.name == requested_name)
    {
        unique_ptr<object> obj = c.create_object();
        obj.owner = this;
        return std::move(obj);
    }

यह पढ़ना कठिन है, क्योंकि यह स्पष्ट नहीं है कि निकाय कोड को केवल एक बार निष्पादित किया जाएगा। इस मामले में, लुकअप को अलग करना बेहतर होगा:

creator &lookup(string const &requested_name)
{
    for(creator &c : creators)
        if(c.name == requested_name)
            return c;
}

creator &c = lookup(requested_name);
unique_ptr obj = c.create_object();

अभी भी एक के ifभीतर है for, लेकिन संदर्भ से यह स्पष्ट हो जाता है कि यह क्या करता है, इस कोड को बदलने की कोई आवश्यकता नहीं है जब तक कि लुकअप नहीं बदलता (जैसे map), और यह तुरंत स्पष्ट है कि create_object()केवल एक बार कहा जाता है, क्योंकि यह लूप के अंदर नहीं।


मुझे यह पसंद है, एक विचारशील और संतुलित अवलोकन के रूप में, भले ही यह एक अर्थ में प्रश्न का उत्तर देने से इनकार करता है। मुझे लगता है कि for( range ){ if( condition ){ action } }-स्टाइल एक समय में एक ही चीज़ों को पढ़ना आसान बनाता है और केवल मूल भाषा निर्माणों के ज्ञान का उपयोग करता है।
PJTraill

@PJTraill, जिस तरह से सवाल किया गया था, उसने मुझे रेमंड चेन के शेख़ी के लिए याद दिलाया , जो कि एंटी-एंटिपर्टेन के लिए था , जो कार्गो-कल्टेड है और किसी तरह निरपेक्ष बन गया है। मैं पूरी तरह से सहमत हूं कि for(...) if(...) { ... }अक्सर सबसे अच्छा विकल्प होता है (यही कारण है कि मैंने कार्रवाई को एक सबरूटीन में विभाजित करने के लिए सिफारिश की है)।
साइमन रिक्टर

1
इस लिंक के लिए धन्यवाद, जिसने मेरे लिए चीजों को स्पष्ट किया: " फॉर- इफ" नाम भ्रामक है, और " फॉर-ऑल-इफ-वन " या " लुकअप-अवॉइडेंस " जैसा कुछ होना चाहिए । यह मुझे उस तरह से याद दिलाता है जैसे 2005 में विकिपीडिया द्वारा अमूर्त उलटा वर्णन किया गया था जब एक " जटिल के शीर्ष पर सरल निर्माण करता है " (जब तक) - जब तक मैं इसे दोबारा नहीं लिखता! वास्तव में मैं लुकअप-प्रोसेस-एग्जिट फॉर्म को ठीक करने के लिए जल्दी नहीं करता अगर यह एकमात्र स्थान था जहां लुकअप हुआ था। for(…)if(…)…
PJTraill

17

यहाँ एक त्वरित अपेक्षाकृत न्यूनतम है filter कार्य है।

यह एक विधेय लेता है। यह एक फ़ंक्शन ऑब्जेक्ट देता है जो एक पुनरावृत्ति लेता है।

यह एक पुनरावृत्ति देता है जिसका उपयोग for(:)लूप में किया जा सकता है ।

template<class It>
struct range_t {
  It b, e;
  It begin() const { return b; }
  It end() const { return e; }
  bool empty() const { return begin()==end(); }
};
template<class It>
range_t<It> range( It b, It e ) { return {std::move(b), std::move(e)}; }

template<class It, class F>
struct filter_helper:range_t<It> {
  F f;
  void advance() {
    while(true) {
      (range_t<It>&)*this = range( std::next(this->begin()), this->end() );
      if (this->empty())
        return;
      if (f(*this->begin()))
        return;
    }
  }
  filter_helper(range_t<It> r, F fin):
    range_t<It>(r), f(std::move(fin))
  {
      while(true)
      {
          if (this->empty()) return;
          if (f(*this->begin())) return;
          (range_t<It>&)*this = range( std::next(this->begin()), this->end() );
      }
  }
};

template<class It, class F>
struct filter_psuedo_iterator {
  using iterator_category=std::input_iterator_tag;
  filter_helper<It, F>* helper = nullptr;
  bool m_is_end = true;
  bool is_end() const {
    return m_is_end || !helper || helper->empty();
  }

  void operator++() {
    helper->advance();
  }
  typename std::iterator_traits<It>::reference
  operator*() const {
    return *(helper->begin());
  }
  It base() const {
      if (!helper) return {};
      if (is_end()) return helper->end();
      return helper->begin();
  }
  friend bool operator==(filter_psuedo_iterator const& lhs, filter_psuedo_iterator const& rhs) {
    if (lhs.is_end() && rhs.is_end()) return true;
    if (lhs.is_end() || rhs.is_end()) return false;
    return lhs.helper->begin() == rhs.helper->begin();
  }
  friend bool operator!=(filter_psuedo_iterator const& lhs, filter_psuedo_iterator const& rhs) {
    return !(lhs==rhs);
  }
};
template<class It, class F>
struct filter_range:
  private filter_helper<It, F>,
  range_t<filter_psuedo_iterator<It, F>>
{
  using helper=filter_helper<It, F>;
  using range=range_t<filter_psuedo_iterator<It, F>>;

  using range::begin; using range::end; using range::empty;

  filter_range( range_t<It> r, F f ):
    helper{{r}, std::forward<F>(f)},
    range{ {this, false}, {this, true} }
  {}
};

template<class F>
auto filter( F&& f ) {
    return [f=std::forward<F>(f)](auto&& r)
    {
        using std::begin; using std::end;
        using iterator = decltype(begin(r));
        return filter_range<iterator, std::decay_t<decltype(f)>>{
            range(begin(r), end(r)), f
        };
    };
};

मैंने शॉर्ट कट लिया। एक वास्तविक पुस्तकालय को वास्तविक for(:)पुनरावृत्तियों को बनाना चाहिए, न कि -क्वालिफ़ाइंग छद्म-फासिड्स जो मैंने किए।

उपयोग के बिंदु पर, यह इस तरह दिखता है:

int main()
{
  std::vector<int> test = {1,2,3,4,5};
  for( auto i: filter([](auto x){return x%2;})( test ) )
    std::cout << i << '\n';
}

जो बहुत अच्छा है, और प्रिंट

1
3
5

जीवंत उदाहरण

C ++ का प्रस्तावित जोड़ है जिसे Rangesv3 कहा जाता है जो इस तरह की बात करता है और बहुत कुछ करता है। boostइसमें फिल्टर रेंज / पुनरावृत्त भी उपलब्ध हैं। बूस्ट में मददगार भी होते हैं जो ऊपर लिखे को बहुत कम लिखते हैं।


15

एक शैली जिसका उल्लेख करने के लिए पर्याप्त उपयोग किया जाता है, लेकिन अभी तक इसका उल्लेख नहीं किया गया है:

for(int i=0; i<myCollection.size(); i++) {
  if (myCollection[i] != SOMETHING)
    continue;

  DoStuff();
}

लाभ:

  • DoStuff();जब स्थिति जटिलता बढ़ती है तो इंडेंटेशन स्तर नहीं बदलता है । तार्किक रूप से, लूप DoStuff();के शीर्ष-स्तर पर होना चाहिए for, और यह है।
  • तुरंत यह स्पष्ट कर देता है कि लूप SOMETHINGसंग्रह के एस से अधिक पुनरावृत्ति करता है , पाठक को यह सत्यापित करने की आवश्यकता के बिना कि ब्लॉक के समापन }के बाद कुछ भी नहीं है if
  • किसी भी पुस्तकालयों या सहायक मैक्रोज़ या कार्यों की आवश्यकता नहीं है।

नुकसान:

  • continue, अन्य प्रवाह नियंत्रण कथनों की तरह, उन तरीकों का दुरुपयोग होता है जो हार्ड-टू-फॉलो कोड के लिए इतना अधिक होता है कि कुछ लोग उनके किसी भी उपयोग के विरोध में होते हैं: कोडिंग की एक वैध शैली है जो कुछ का पालन करती है जो बच जाती है continue, जो breakअन्य से बचती है में switch, कि returnएक समारोह के अंत के अलावा अन्य से बचा जाता है।

3
मेरा तर्क है कि एक forलूप में जो कई लाइनों तक चलता है, एक दो-लाइन "यदि नहीं, तो जारी रखें" बहुत स्पष्ट, तार्किक और पठनीय है। तुरंत कह रहा है, "इसे छोड़ें अगर" के बादfor कथन को अच्छी तरह से पढ़ने के और जैसा कि आपने कहा था, लूप के शेष कार्यात्मक पहलुओं पर ध्यान नहीं देता है। यदि continueआगे और नीचे किया जाता है, हालांकि, कुछ स्पष्टता का बलिदान किया जाता है (अर्थात यदि कुछ ऑपरेशन हमेशा ifकथन से पहले किया जाएगा )।
गुमनाम

11
for(auto const &x: myCollection) if(x == something) doStuff();

एक सी ++ की तरह बहुत ज्यादा लगता है - forमेरे लिए विशिष्ट समझ। आप को?


मुझे नहीं लगता कि ऑटो कीवर्ड c ++ 11 से पहले मौजूद था इसलिए मैं यह नहीं कहूंगा कि यह बहुत ही शास्त्रीय c ++ है। यदि मैं टिप्पणी में यहाँ एक प्रश्न पूछ सकता हूं, तो क्या "ऑटो कास्ट" संकलक को बताएगा कि वह सभी तत्वों को पुनर्व्यवस्थित कर सकता है जैसा वह चाहता है? हो सकता है कि संकलक के लिए यह आसान हो जाए कि अगर ऐसा हो तो ब्रांचिंग से बचने की योजना बनाएं।
गणितज्ञ

1
@ मैथ्रेडलर जितनी जल्दी लोग "शास्त्रीय सी ++" के बारे में चिंता करना बंद कर देते हैं, उतना बेहतर है। C ++ 11 भाषा के लिए एक मैक्रोएवोल्यूशनरी इवेंट था और 5 साल पुराना है: यह न्यूनतम होना चाहिए जिसके लिए हम प्रयास करते हैं। वैसे भी, ओपी ने टैग किया कि और सी ++ 14 (और भी बेहतर!)। नहीं, auto constपुनरावृत्ति क्रम पर कोई असर नहीं पड़ता है। यदि आप राउंड-बेस्ड दिखते हैं for, तो आप देखेंगे कि यह मूल रूप से एक अंतर्निहित लूप begin()है end()जिसका निहित प्रभाव है। कोई तरीका नहीं है कि यह कंटेनर की क्रमिक गारंटी (यदि कोई हो) को तोड़ सकता है; यह पृथ्वी के चेहरे से दूर हँसे किया गया है जाएगा
underscore_d

1
@ मैथ्रेडलर, वास्तव में यह था, बस इसका काफी अलग अर्थ था। जो मौजूद नहीं था वह है रेंज-फॉर ... और कोई अन्य विशिष्ट C ++ 11 फ़ीचर। क्या मैं यहाँ का मतलब है कि सीमा-fors, था std::futureरों, std::functionरों, यहां तक कि उन गुमनाम बंद बहुत अच्छी तरह से कर रहे हैं सी ++ वाक्य रचना में ish; हर भाषा का अपना अलग अर्थ होता है और जब नई विशेषताओं को शामिल किया जाता है तो यह उन्हें पुराने प्रसिद्ध वाक्यविन्यास की नकल करने की कोशिश करता है।
बिप्लब

@underscore_d, एक संकलक को किसी भी परिवर्तन को करने की अनुमति दी जाती है, बशर्ते कि नियम का पालन किया जाए, है न?
बिप्लब

1
हम्म्, और संभवतः इसका क्या मतलब हो सकता है?
बिप्लब

7

अगर DoStuff () भविष्य में किसी तरह मैं पर निर्भर होगा, तो मैं इस गारंटीकृत शाखा-मुक्त बिट-मास्किंग संस्करण का प्रस्ताव दूंगा।

unsigned int times = 0;
const int kSize = sizeof(unsigned int)*8;
for(int i = 0; i < myCollection.size()/kSize; i++){
  unsigned int mask = 0;
  for (int j = 0; j<kSize; j++){
    mask |= (myCollection[i*kSize+j]==SOMETHING) << j;
  }
  times+=popcount(mask);
}

for(int i=0;i<times;i++)
   DoStuff();

जहां पॉपकाउंट किसी भी फ़ंक्शन को जनसंख्या गणना (बिट्स की संख्या = 1) कर रहा है। I और उनके पड़ोसियों के साथ अधिक उन्नत बाधाओं को रखने के लिए कुछ स्वतंत्रता होगी। अगर इसकी जरूरत नहीं है तो हम आंतरिक लूप को हटा सकते हैं और बाहरी लूप को रीमेक कर सकते हैं

for(int i = 0; i < myCollection.size(); i++)
  times += (myCollection[i]==SOMETHING);

इसके बाद ए

for(int i=0;i<times;i++)
   DoStuff();

6

इसके अलावा, यदि आप संग्रह को पुन: व्यवस्थित करने में लापरवाही नहीं करते हैं, तो std :: विभाजन सस्ता है।

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

void DoStuff(int i)
{
    std::cout << i << '\n';
}

int main()
{
    using namespace std::placeholders;

    std::vector<int> v {1, 2, 5, 0, 9, 5, 5};
    const int SOMETHING = 5;

    std::for_each(v.begin(),
                  std::partition(v.begin(), v.end(),
                                 std::bind(std::equal_to<int> {}, _1, SOMETHING)), // some condition
                  DoStuff); // action
}

लेकिन std::partitionकंटेनर को पुन: व्यवस्थित करता है।
celtschk

5

मैं उपरोक्त समाधानों की जटिलता से आश्चर्य में हूं। मैं एक साधारण सुझाव देने के लिए जा रहा था #define foreach(a,b,c,d) for(a; b; c)if(d), लेकिन यह कुछ स्पष्ट घाटे, उदाहरण के लिए, आप अपने पाश में अर्धविराम के बजाय अल्पविराम का उपयोग करने के लिए याद करने के लिए है है, और आप में अल्पविराम ऑपरेटर का उपयोग नहीं कर सकते हैं aया c

#include <list>
#include <iostream>

using namespace std; 

#define foreach(a,b,c,d) for(a; b; c)if(d)

int main(){
  list<int> a;

  for(int i=0; i<10; i++)
    a.push_back(i);

  for(auto i=a.begin(); i!=a.end(); i++)
    if((*i)&1)
      cout << *i << ' ';
  cout << endl;

  foreach(auto i=a.begin(), i!=a.end(), i++, (*i)&1)
    cout << *i << ' ';
  cout << endl;

  return 0;
}

3
कुछ उत्तरों की जटिलता केवल उच्च है क्योंकि वे पहले एक पुन: प्रयोज्य सामान्य विधि (जो आप केवल एक बार करते हैं) दिखाते हैं और फिर उसका उपयोग करते हैं। प्रभावी नहीं है अगर आपके पास एक लूप है यदि आपके पूरे आवेदन में एक शर्त है, लेकिन बहुत प्रभावी है यदि यह एक हजार बार होता है।
gnasher729

1
अधिकांश सुझावों की तरह, यह कठिन, आसान नहीं है, सीमा और चयन स्थिति की पहचान करने के लिए। और मैक्रो के उपयोग के बारे में अनिश्चितता बढ़ जाती है जब (और कितनी बार) अभिव्यक्तियों का मूल्यांकन किया जाता है, भले ही यहां कोई आश्चर्य न हो।
PJTraill

2

I: s महत्वपूर्ण हैं। यह एक सूची बनाता है जो इंडेक्स में भरता है जिसके लिए doStuff () कॉल करना है। एक बार फिर से मुख्य बिंदु ब्रांचिंग से बचना है और इसे पाइपेलनेबल अंकगणितीय लागतों के लिए व्यापार करना है।

int buffer[someSafeSize];
int cnt = 0; // counter to keep track where we are in list.
for( int i = 0; i < container.size(); i++ ){
   int lDecision = (container[i] == SOMETHING);
   buffer[cnt] = lDecision*i + (1-lDecision)*buffer[cnt];
   cnt += lDecision;
}

for( int i=0; i<cnt; i++ )
   doStuff(buffer[i]); // now we could pass the index or a pointer as an argument.

"जादुई" लाइन बफर लोडिंग लाइन है जो मूल्य को बनाए रखने और स्थिति में रहने या स्थिति को गिनने और मूल्य जोड़ने के लिए अंकगणित की गणना करती है। इसलिए हम कुछ लॉजिक्स और अंकगणित और शायद कुछ कैश हिट के लिए एक संभावित शाखा को दूर करते हैं। एक विशिष्ट परिदृश्य जब यह उपयोगी होगा यदि doStuff () पाइपलाइन करने योग्य गणना की थोड़ी मात्रा करता है और कॉल के बीच में कोई भी शाखा उन पाइपलाइनों को बाधित कर सकती है।

फिर बफर पर बस लूप करें और तब तक डस्टफ () चलाएं जब तक हम सेंटी तक नहीं पहुंच जाते। इस बार हमारे पास बफर में स्टोर किया गया i चालू होगा इसलिए हम कॉल में इसे doStuff () में उपयोग कर सकते हैं यदि हमें आवश्यकता होगी।


1

किसी श्रेणी के सबसेट पर, या दूसरे शब्दों में कुछ फ़ंक्शन को लागू करने के रूप में आपके कोड पैटर्न का वर्णन किया जा सकता है: इसे पूरी रेंज में फ़िल्टर लागू करने के परिणाम पर लागू करना।

यह एरिक नीब्लर की पर्वतमाला-वी 3 पुस्तकालय के साथ सबसे सरल तरीके से प्राप्त करने योग्य है ; हालाँकि यह थोड़ा आंखों का काम है, क्योंकि आप सूचकांकों के साथ काम करना चाहते हैं:

using namespace ranges;
auto mycollection_has_something = 
    [&](std::size_t i) { return myCollection[i] == SOMETHING };
auto filtered_view = 
    views::iota(std::size_t{0}, myCollection.size()) | 
    views::filter(mycollection_has_something);
for (auto i : filtered_view) { DoStuff(); }

लेकिन अगर आप सूचकांकों को त्यागना चाहते हैं, तो आपको मिलेगा:

auto is_something = [&SOMETHING](const decltype(SOMETHING)& x) { return x == SOMETHING };
auto filtered_collection = myCollection | views::filter(is_something);
for (const auto& x : filtered_collection) { DoStuff(); }

जो अच्छे IMHO है।

PS - रेंज लाइब्रेरी ज्यादातर C ++ 20 में C ++ मानक में जा रही है।


0

मैं सिर्फ माइक एक्टन का उल्लेख करूंगा, वह निश्चित रूप से कहेंगे:

अगर आपको ऐसा करना है, तो आपको अपने डेटा की समस्या है। अपना डेटा सॉर्ट करें!

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.