2010-02-03 30 views
7

Sử dụng phạm vi dựa cho vòng trong C++ 0X, tôi biết chúng tôi sẽ có thể làm:Làm thế nào để cắt với vòng lặp for-range? C++ 0x

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

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

(có thể thậm chí đơn giản để viết với lambda)

Nhưng làm thế nào tôi nên làm gì nếu tôi chỉ muốn áp dụng processNumber (k) cho một phần của số? Ví dụ, làm thế nào tôi nên viết này cho vòng lặp để áp dụng processNumber() cho một nửa (đầu hoặc đuôi) của các con số? Được "cắt" cho phép như trong Python hay Ruby?

+4

Nó có lẽ chỉ là dễ dàng hơn để làm 'std :: for_each (từ, để , [] (int k) {processNumber (k);}); '. Hoặc bạn sẽ phải cung cấp một cho mỗi phạm vi con tương thích trong vectơ đó. –

+0

Vâng, tôi biết điều đó. Tôi chỉ muốn biết các giới hạn của vòng lặp for-range trong C++ so với các ngôn ngữ khác mà việc cắt lát là "dễ dàng". – Klaim

+0

Không phải là giải pháp 'for_each' được hiển thị ở trên" dễ dàng "? – jalf

Trả lời

0

Something như thế này có thể làm việc (không được kiểm soát như tôi không có quyền truy cập vào một trình biên dịch C++ 0x),

Edit: Kiểm tra nó trên VS10, tất nhiên tôi phải sửa chữa lỗi numurous ....

Xác định lớp là proxy cho bất kỳ vùng chứa nào và chỉ iterator s trả lại một tập con của vùng chứa. Ví dụ tôi cung cấp là ví dụ đơn giản nhất cho nửa đầu nhưng nó có thể được làm tổng quát hơn nhiều.

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); } 

Và sau đó bạn sử dụng nó trong vòng lặp:

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

Câu trả lời hay. Lưu ý rằng tên kết thúc bằng '_t' được đặt trước: http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier –

+0

+1. Chỉ cần một vài gợi ý: Bạn nên lấy được lớp lặp từ std :: iterator_traits .Nhưng bạn muốn điều này trở thành một trình lặp đầu vào bất kể Container, vì vậy bạn cũng nên thêm "typedef std :: input_iterator_tag iterator_category" vào phần thân của lớp. Ngoài ra, bạn quên thực hiện toán tử ==. – Manuel

+0

Đó là rất nhiều công việc chỉ để đạt được những gì bạn có thể làm với một oneliner 'for_each'. – jalf

3

Một khả năng có thể thúc đẩy của iterator_range

(Không có một trình biên dịch hỗ trợ phạm vi có trụ sở, sử dụng BOOST_FOREACH thay vào đó tôi. 'd mong đợi dựa trên phạm vi cho công việc giống nhau, miễn là vùng chứa hoặc dải ô có phương thức bắt đầu và kết thúc.)

#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'; 
    } 
} 

Để thuận tiện, bạn cũng có thể thực hiện chức năng lát của riêng mình, vì vậy nó sẽ chấp nhận các chỉ mục thay vì các trình lặp. Một lần nữa, nó có thể được dựa trên boost.iterator_range, hay không:

#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'; 
} 

Nói chung người ta sẽ có thể được làm việc với lặp ở nơi đầu tiên, vì vậy nó có thể không phải là hữu ích.

+0

+1 Giải pháp tốt, chắc chắn là cách để đi nếu bạn đang sử dụng để làm việc với các phạm vi (như trái ngược với làm việc với các trình vòng lặp). Một tối ưu hóa tôi sẽ đề nghị là đặt dòng "iterator last = first;" bên dưới dòng tiến lên "đầu tiên". Tất nhiên, điều này có nghĩa là "cuối cùng" phải được nâng cao bởi "i_last-i_first", không phải "i_last". – Manuel

+0

Có điều tuyệt vời nhưng những gì tôi đang tìm kiếm là một cách dựa trên vòng lặp dựa trên việc thực hiện nó. Tôi cảm thấy rằng vòng lặp for-range có thể là "không đầy đủ" mà không cần cắt nhưng có lẽ tôi chỉ không biết làm thế nào để cắt với cú pháp mới này? – Klaim

+1

@Kêu cầu: Nó có hoạt động với phạm vi không? Tôi không thể kiểm tra và tôi không biết bất kỳ cú pháp cắt đặc biệt mới nào. Theo như tôi biết, bất cứ điều gì cung cấp một phương thức start() và end() trả về giống như iterator. @Manuel: Cảm ơn, đã chỉnh sửa mã. – UncleBens

11

Bạn có thể sử dụng "sliced" range adaptor từ thư viện Boost.Range:

#include <boost/range/adaptor/sliced.hpp> 

using boost::adaptors::sliced; 

...

std::vector<int> numbers = generateNumbers(); 
for(int k : numbers | sliced(0, numbers.size()/2)) 
{ 
    processNumber(k); 
} 
Các vấn đề liên quan