2012-11-01 8 views
8

Pythonのitertoolsは、n-褶壁形成反復可能オブジェクトのためのteeを持っていますitertools.teeはBoost :: Rangeに相当しますか?

def tee(iterable, n=2): 
    it = iter(iterable) 
    deques = [collections.deque() for i in range(n)] 
    def gen(mydeque): 
     while True: 
      if not mydeque:    # when the local deque is empty 
       newval = next(it)  # fetch a new value and 
       for d in deques:  # load it to all the deques 
        d.append(newval) 
      yield mydeque.popleft() 
    return tuple(gen(d) for d in deques) 

私はBoost::Rangeに相当するものを見つけることができませんでした。私は何かを逃しているのですか?

+3

あなたが望むもののより良いプレーンテキストの説明を追加することもできます。誰もが収穫やリストの表現が何をPythonで行うのか理解できない場合があります。 – inf

+0

これにはユースケースがありますか? – filmor

+0

これはしばらくしていましたが、アイデアは、イテレータをさまざまな操作を適用する一連のコンシューマ(たとえば異なるスレッド)にn-plicateすることでした。 Unixの 'tee'コマンドに似ています。私は入力を複製するのではなく、順次操作を適用するだけで(つまりそれらを多重化する)全く異なるアプローチを採用しました。 –

答えて

1

itertools.teeは、Pythonのユビキタスなシングルパスのイテラブルに適しています。たとえば、Generatorsはシングルパスで、よく使用されます。あなたはすでにそれが余計な重複を伴うだろうので、あなたは、それのためのitertools.tee使用することはありませんリスト/両端キューを持っている場合

は、しかし - あなただけ何度も何度も元のリスト/デックを反復処理することができます。

C++はシングルパス範囲の概念も持っています。たとえばInput Iteratorですが、それらはあまり普及していません。これは典型的なC++プログラムの目的の別のセットの結果であり、可能な限り最高のパフォーマンスを維持しながらユーザーに最大限与えます。もしあなたが望むなら別の考え方です。これはのはboost::transformed及びitertools.imap(又はgenerator expressions)を比較してみましょう例示する

それらの両方が与えられた「プリズム」を介して入力シーケンスのビューを提供します。 itertools.imap戻り、単一パスのiterable、ブースト::変換リターンは入力レンジと同じカテゴリ持つビューの範囲ながら - あなたは結果としてランダムアクセス範囲になるだろう、入力としてRandom Access Rangeを通過する場合、すなわちを。

もう一つの事実は、C++はデフォルトでvalue semanticsを採用していますが、pythonはポインタの意味を持っています。つまり、C++のコピーイテレータを複数回「バンプ」すると、元のイテレータは変更されません(ただし、シングルパス範囲の場合は無効にできますが、ポイントではありません)。

ただし、シングルパス範囲の値を累積して何回か見たい場合があります。そのような場合、最も一般的な解決方法は、手で明示的に値をいくつかのコンテナに累積することです。例えば:

vector<int> cache(first,last); 

しかし、ティーのようなラッパーはC++でまだ可能であり、ここにある概念実証。使用方法は、次のとおり

auto forward_range = tee_range(first,last); 

tee_rangeは、引数としてシングルパス範囲を取り、(イテレータレベルで動作make_tee_iteratorは、もある)(マルチパス)で前進レンジを返します。だから、あなたは何回かその範囲のコピーを取り、それを繰り返すことができます。 itertools.tee

auto r = forward_range; 
auto out = ostream_iterator<int>(cout," "); 
copy(forward_range,out); 
copy(forward_range,out); 
copy(r,out); 

Thererもimprovenmentされる - 内部で、一つだけ両端キューがキャッシュ値に使用されています。

live demo

#include <boost/range/adaptor/transformed.hpp> 
#include <boost/iterator/iterator_facade.hpp> 
#include <boost/smart_ptr/make_shared.hpp> 
#include <boost/range/iterator_range.hpp> 
#include <boost/smart_ptr/shared_ptr.hpp> 
#include <boost/container/vector.hpp> 
#include <boost/container/deque.hpp> 
#include <boost/range/algorithm.hpp> 
#include <algorithm> 
#include <iterator> 
#include <cassert> 
#include <limits> 

template<typename InputIterator> 
class tee_iterator : public boost::iterator_facade 
    < 
     tee_iterator<InputIterator>, 
     const typename std::iterator_traits<InputIterator>::value_type, 
     boost::forward_traversal_tag 
    > 
{ 
    typedef typename std::iterator_traits<InputIterator>::value_type Value; 
    typedef unsigned Index; 
    struct Data 
    { 
     boost::container::deque<Value> values; 
     boost::container::vector<tee_iterator*> iterators; 
     InputIterator current,end; 
     Index min_index, current_index; 
     Index poped_from_front; 
     // 
     Data(InputIterator first,InputIterator last) 
      : current(first), end(last), min_index(0), current_index(0), poped_from_front(0) 
     {} 
     ~Data() 
     { 
      assert(iterators.empty()); 
     } 
    }; 
    boost::shared_ptr<Data> shared_data; 
    Index index; 
    static Index get_index(tee_iterator *p) 
    { 
     return p->index; 
    } 
public: 
    tee_iterator() 
     : index(std::numeric_limits<Index>::max()) 
    {} 
    tee_iterator(InputIterator first,InputIterator last) 
     : shared_data(boost::make_shared<Data>(first,last)), index(0) 
    { 
     shared_data->iterators.push_back(this); 
    } 
    tee_iterator(const tee_iterator &x) 
     : shared_data(x.shared_data), index(x.index) 
    { 
     if(shared_data) 
      shared_data->iterators.push_back(this); 
    } 
    friend void swap(tee_iterator &l,tee_iterator &r) 
    { 
     using std::swap; 
     *boost::find(l.shared_data->iterators,&l) = &r; 
     *boost::find(r.shared_data->iterators,&r) = &l; 
     swap(l.shared_data,r.shared_data); 
     swap(l.index,r.index); 
    } 
    tee_iterator &operator=(tee_iterator x) 
    { 
     swap(x,*this); 
    } 
    ~tee_iterator() 
    { 
     if(shared_data) 
     { 
      erase_from_iterators(); 
      if(!shared_data->iterators.empty()) 
      { 
       using boost::adaptors::transformed; 
       shared_data->min_index = *boost::min_element(shared_data->iterators | transformed(&get_index)); 
       Index to_pop = shared_data->min_index - shared_data->poped_from_front; 
       if(to_pop>0) 
       { 
        shared_data->values.erase(shared_data->values.begin(), shared_data->values.begin()+to_pop); 
        shared_data->poped_from_front += to_pop; 
       } 
      } 
     } 
    } 
private: 
    friend class boost::iterator_core_access; 
    void erase_from_iterators() 
    { 
     shared_data->iterators.erase(boost::find(shared_data->iterators,this)); 
    } 
    bool last_min_index() const 
    { 
     return boost::count 
     (
      shared_data->iterators | boost::adaptors::transformed(&get_index), 
      shared_data->min_index 
     )==1; 
    } 
    Index obtained() const 
    { 
     return Index(shared_data->poped_from_front + shared_data->values.size()); 
    } 
    void increment() 
    { 
     if((shared_data->min_index == index) && last_min_index()) 
     { 
      shared_data->values.pop_front(); 
      ++shared_data->min_index; 
      ++shared_data->poped_from_front; 
     } 
     ++index; 
     if(obtained() <= index) 
     { 
      ++shared_data->current; 
      if(shared_data->current != shared_data->end) 
      { 
       shared_data->values.push_back(*shared_data->current); 
      } 
      else 
      { 
       erase_from_iterators(); 
       index=std::numeric_limits<Index>::max(); 
       shared_data.reset(); 
      } 
     } 
    } 
    bool equal(const tee_iterator& other) const 
    { 
     return (shared_data.get()==other.shared_data.get()) && (index == other.index); 
    } 
    const Value &dereference() const 
    { 
     if((index==0) && (obtained() <= index)) 
     { 
      shared_data->values.push_back(*(shared_data->current)); 
     } 
     assert((index-shared_data->poped_from_front) < shared_data->values.size()); 
     return shared_data->values[index-shared_data->poped_from_front]; 
    } 
}; 

template<typename InputIterator> 
tee_iterator<InputIterator> make_tee_iterator(InputIterator first,InputIterator last) 
{ 
    return tee_iterator<InputIterator>(first,last); 
} 

template<typename InputIterator> 
boost::iterator_range< tee_iterator<InputIterator> > tee_range(InputIterator first,InputIterator last) 
{ 
    return boost::iterator_range< tee_iterator<InputIterator> > 
    (
     tee_iterator<InputIterator>(first,last), 
     tee_iterator<InputIterator>() 
    ); 
} 
// _______________________________________________________ // 

#include <iostream> 
#include <ostream> 
#include <sstream> 

int main() 
{ 
    using namespace std; 
    stringstream ss; 
    ss << "1 2 3 4 5"; 
    istream_iterator<int> first(ss /*cin*/),last; 
    typedef boost::iterator_range< tee_iterator< istream_iterator<int> > > Range; // C++98 
    Range r1 = tee_range(first,last); 
    Range r2 = r1, r3 = r1; 
    boost::copy(r1,ostream_iterator<int>(cout," ")); 
    cout << endl; 
    boost::copy(r2,ostream_iterator<int>(cout," ")); 
    cout << endl; 
    boost::copy(r2,ostream_iterator<int>(cout," ")); 
} 

出力は次のとおりです。

1 2 3 4 5 
1 2 3 4 5 
1 2 3 4 5 

Boost.Spiritは、同様の目的を持つMulti Pass iteratorを持っています。

multi_passイテレータは、入力イテレータをSpirit.Qiでの使用に適した順方向イテレータに変換します。 multi_passは必要なときにデータをバッファし、その内容がもはや必要でないときにバッファを破棄します。これは、イテレータのコピーが1つだけ存在する場合、またはバックトラッキングが発生しない場合に発生します。

関連する問題