2010-02-03 14 views
7

, ne yapmamız mümkün olacak biliyorum:Aralık aralığı ile dilim nasıl? C++ 0 X döngüler için esaslı aralığını kullanarak C++ 0x

std::vector<int> numbers = generateNumbers(); 

for(int k : numbers) 
{ 
    processNumber(k); 
} 

(lambda'da yazmaya daha basit olabilir)

Ama nasıl processNumber (k) 'nin sadece sayıların bir kısmına uygulanmasını istiyorsam bunu yapmalı mıyım? Örneğin, processNumber() öğesini sayıların yarısına (baş veya kuyruğuna) uygulamak için bu döngüye nasıl yazmalıyım? Python veya Ruby'de olduğu gibi "dilimleme" izni var mı?, (Ben C++ 0x derleyiciye erişimi olmayan olarak kontrolsüz) böyle

+4

#include <boost/range/adaptor/sliced.hpp> using boost::adaptors::sliced; 

... 'Std :: for_each (den önce yapmanız , [] (int k) {processNumber (k);}); '. Ya da bu vektördeki her bir uyumlu alt aralık için bir tane sağlamanız gerekir. –

+0

Evet, bunun farkındayım. Sadece dilimlemenin "kolay" olduğu diğer dillere kıyasla C++ 'daki menzil döngüsünün sınırlarını bilmek istiyorum. – Klaim

+0

Yukarıda "for_each" çözümü "kolay" değil mi? – jalf

cevap

0

şey çalışabilir

Düzenleme: Ben numurous hataları düzeltmek zorunda elbette VS10 üzerine İşaretli ....

olan iterator sadece kabın bir alt kümesini döndürür s herhangi kaba bir proxy bir sınıf tanımlamak ve. Tedarik ettiğim örnek ilk yarısını veren en basit olanıdır, ancak daha genel hale getirilebilir.

template <class Container> 
class head_t { 
    Container& c_; 
public: 
    template <class T> 
    class iter { 
     T curr_; 
     const T& end_; 
     int limit_; // count how many items iterated 
    public: 
     iter(T curr, const T& end) 
      : curr_(curr) 
      , end_(end)    
      , limit_(std::distance(curr_, end_)/2) 
      { } 

     typename Container::value_type operator*() { return *curr_; } 

     // Do the equivilant for for operator++(int) 
     iter& operator++() { 
      if (--limit_ == 0) // finished our slice 
       curr_ = end_; 
      else 
       ++curr_; 
      return *this; 
     } 

     bool operator!=(const iter& i) const { 
      return curr_ != i.curr_; 
     } 
    }; 

    head_t(Container& c) : c_(c) {} 
    iter<typename Container::iterator> begin() { 
     return iter<typename Container::iterator>(c_.begin(), c_.end()); 
    } 

    iter<typename Container::iterator> end() { 
     return iter<typename Container::iterator>(c_.end(), c_.end()); 
    }  
}; 

template <class T> 
head_t<T> head(T& t) { return head_t<T>(t); } 

Ve sonra döngüde kullanmak:

for(int k : head(numbers)) 
+0

İyi cevap. '_t' ile biten isimlerin ayrıldığını unutmayın: http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier –

+0

+1. Sadece birkaç öneri: Iter sınıfını std :: iterator_traits 'dan türetmelisiniz.Ancak bunun, kapsayıcıdan bağımsız olarak bir giriş yineleyicisi olmasını istiyorsunuz, böylece sınıf gövdesine "typedef std :: input_iterator_tag iterator_category" de eklemelisiniz. Ayrıca, operatörü uygulamayı unutmuşsunuz ==. – Manuel

+0

Bu sadece bir "for_each" oneliner ile yapabileceğiniz şeyleri başarmak için çok iş var. – jalf

3

Bir olasılık olabilir boost en iterator_range

(yerine BOOST_FOREACH kullanarak, için aralık tabanlı destekleyen bir derleyici sahip değil ben. 'konteyner sürece, aralık tabanlı işlemi için alınan beklemek ya da d aralığı başlangıç ​​ve bitiş yöntemi.)

#include <boost/foreach.hpp> 
#include <boost/range/iterator_range.hpp> 
#include <iostream> 
#include <vector> 

int main() 
{ 
    std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 
    BOOST_FOREACH(int n, boost::make_iterator_range(v.begin(), v.begin() + v.size()/2)) { 
     std::cout << n << '\n'; 
    } 
} 
01 sahiptir

Kolaylık için kendi dilim işlevinizi de yapabilirsiniz, bu nedenle yineleyiciler yerine dizin kabul eder. Yine, boost.iterator_range dayalı ya da ihmal edilemezdi:

#include <cstddef> 
#include <iterator> 

template <class Iterator> 
class iter_pair 
{ 
public: 
    typedef Iterator iterator; 
    typedef Iterator const_iterator; //BOOST_FOREACH appears to want this 
    iter_pair(iterator first, iterator last): first(first), last(last) {} 
    iterator begin() const { return first; } 
    iterator end() const { return last; } 
private: 
    iterator first, last; 
}; 

template <class Container> 
struct iterator_type 
{ 
    typedef typename Container::iterator type; 
}; 

template <class Container> 
struct iterator_type<const Container> 
{ 
    typedef typename Container::const_iterator type; 
}; 

template <class Container> 
iter_pair<typename iterator_type<Container>::type> 
    slice(Container& c, size_t i_first, size_t i_last) 
{ 
    typedef typename iterator_type<Container>::type iterator; 
    iterator first = c.begin();   
    std::advance(first, i_first); 
    iterator last = first; 
    std::advance(last, i_last - i_first); 
    return iter_pair<iterator>(first, last); 
} 

template <class Container> 
iter_pair<typename iterator_type<Container>::type> 
    slice(Container& c, size_t i_last) 
{ 
    return slice(c, 0, i_last); 
} 

//could probably also be overloaded for arrays 

#include <cctype> 
#include <string> 
#include <boost/foreach.hpp> 
#include <iostream> 

int main() 
{ 
    std::string s("Hello world, la-la-la!"); 
    BOOST_FOREACH(char& c, slice(s, 2, 11)) { 
     if (c == 'l') 
      c = std::toupper(c); 
    } 
    const std::string& r = s; 
    BOOST_FOREACH(char c, slice(r, r.size() - 1)) { 
     std::cout << c << " "; 
    } 
    std::cout << '\n'; 
} 

Genellikle biri muhtemelen ilk etapta adım adım elde çalışma olacağını, bu nedenle bu faydalı olmayabilir.

+0

+1 İyi bir çözüm, kesinlikle aralıklarla çalışmak için alıştığınız yoldur (yineleyicilerle çalışmak yerine). Önereceğim bir optimizasyon "iterator last = first; "İlk" ilerleyen çizginin altında. Tabii ki, bu "son", "i_last" değil, "i_last-i_first" ile ilerletilmesi gerektiği anlamına gelir. – Manuel

+0

Evet harika ama aradığım şey, bunu yapmak için bir dizi döngü tabanlı yoludur. Ben aralıklı döngü dilimleme olmadan "eksik" olabileceğini hissediyorum ama belki bu yeni sözdizimi ile dilimleme nasıl yapacağını bilmiyorum? – Klaim

+1

@Klaim: Bu seri ile çalışmıyor mu? Test edemem ve yeni özel dilimleme sözdiziminin farkında değilim. Bildiğim kadarıyla, yineleyici gibi şeyler döndüren bir begin() ve end() yöntemi sağlayan bir şey gider. @Manuel: Teşekkürler, düzenlenmiş kod. – UncleBens

11

Sen Boost.Range kütüphanesinden "sliced" range adaptor kullanabilirsiniz: Muhtemelen sadece daha kolay olurdu

std::vector<int> numbers = generateNumbers(); 
for(int k : numbers | sliced(0, numbers.size()/2)) 
{ 
    processNumber(k); 
} 
İlgili konular