2009-10-31 53 views
0

我的问题比这个更复杂,所以我将它缩小到一个非常简单的例子,足以让我知道如何处理其余的问题。转换多个迭代器元素

说我有一个输入迭代器。我想创建一个新的输入迭代器,其中每个元素是原始输入的多个顺序元素的组合,并且具有以下模式。运行长度在输入序列中编码。

输入: { 1 1 2 3 4 4 6 7 8 9 ... }

输出: { (1) (3+4) (6+7+8+9) ... }

我想是这样的函数可以处理单个元件并递增输入开始迭代器(通过引用传递)。在我的评论中有几个问题,另外我想知道是否有一个好的方法来处理整个元素流。

编辑:我知道有一个在调用std::advance其中tmp迭代器递增为准确end,这将是适用于该代码中的bug。让我们把重点放在我的其余问题上,我会解决这个问题。 编辑2:应该现在修复?

template<class TInputIterator, class TOutputIterator> 
void process_single(TInputIterator& begin, TInputIterator end, TOutputIterator destination) 
{ 
    std::iterator_traits<TInputIterator>::value_type run_length = *begin; 
    ++begin; 

    // is there a better way to specify run_length elements to accumulate() without having to call advance() here? 
    TInputIterator tmp(begin); 
    std::advance(tmp, run_length); 
    // Edited: this condition should work for the different kinds of iterators? 
    if ((end < tmp) || (std::distance(begin, tmp) != run_length)) 
     throw std::range_error("The input sequence had too few elements."); 

    // std::plus is the default accumulate function 
    *destination = std::accumulate(begin, tmp, 0/*, std::plus<TInputIterator::value_type>()*/); 

    // should I use std::swap(begin, tmp) here instead? 
    begin = tmp; 
} 

编辑3:在应对答案,这会是更好?

template<class TInputIterator, class TOutputIterator> 
TInputIterator process_single(TInputIterator begin, TInputIterator end, TOutputIterator destination) 
{ 
    typedef std::iterator_traits<TInputIterator>::value_type value_type; 

    value_type run_length = *begin; 
    ++begin; 

    value_type sum = 0; 
    while (run_length > 0 && begin != end) 
    { 
     sum += *begin; 
     ++begin; 
     --run_length; 
    } 

    if (run_length) 
    { 
     throw std::range_error("The input sequence had too few elements."); 
    } 

    *destination = sum; 

    return begin; 
} 

template<class TInputIterator, class TOutputIterator> 
void process(TInputIterator begin, TInputIterator end, TOutputIterator destination) 
{ 
    while (begin != end) 
    { 
     begin = process_single(begin, end, destination); 
    } 
} 
+1

我老老实实有点困惑的问题是在这里的.. – 2009-11-01 00:11:22

回答

0
// is there a better way to specify run_length elements to accumulate() without having to call advance() here? 

不是真的。

// Edited: this condition should work for the different kinds of iterators? 
if ((end < tmp) || (std::distance(begin, tmp) != run_length)) 
    throw std::range_error("The input sequence had too few elements."); 

这里的问题是<运营商,这只会为RandomAccessIterators工作。为什么不只是:

if (std::distance(tmp, end) < run_length) 

// should I use std::swap(begin, tmp) here instead? 
begin = tmp; 

没有。

EDIT: I'm aware there's a bug in the call to std::advance where the tmp iterator is incremented to be exactly end, which would be valid for this code. Let's focus on the rest of my questions and I'll fix that. 

递增到结束是STL算法的标准行为。

void process_single(TInputIterator& begin, TInputIterator end, TOutputIterator destination) 

STL迭代器通常不是一个好的类型来传递byref。呼叫者经常希望在呼叫你的功能后保存它们。例如,通过按地址导致此不进行编译:

std::vector<something> t; 
std::vector<something> t2; 
process_single(t.begin(), t.end(), std::back_inserter(t2)) 

(许多编译器会把它,但它不是标准)

更好的方式是通过迭代器BYVAL,然后返回新的位置上,你结束你的算法,以便与STL的其他部分更加一致。例如,请参阅std :: find()。

希望有帮助....

2

我会手动编写这个算法。

首先,函数不接受输入迭代器,因为它们不支持advance和distance。

其次,错误检查已关闭。如果我没有弄错,end < tmp的可能性意味着一些未定义的行为已被调用。想象一下容器是一个std :: list。如果你设法推进beyong list.end(),会发生什么?但我认为即使使用向量或数组也是未定义的(并且MSVC++可能会在您之前使用其迭代器调试)。

因此,整个序列进行解码,我会做这样的事情:

#include <iostream> 
#include <algorithm> 
#include <vector> 
#include <stdexcept> 
#include <iterator> 

template <class InputIterator, class OutputIterator> 
void decode(InputIterator start, InputIterator end, OutputIterator output) 
{ 
    typedef typename std::iterator_traits<InputIterator>::value_type value_type; 
    while (start != end) 
    { 
     value_type count = *start; 
     ++start; 
     value_type result = value_type(); 
     for (value_type i = value_type(); i != count; ++i, ++start) { 
      if (start == end) { 
       throw std::range_error("The input sequence had too few elements."); 
      } 
      result += *start; 
     } 
     *output = result; 
     ++output; 
    } 
} 

int main() 
{ 
    try { 
     std::vector<int> v; 
     decode(std::istream_iterator<int>(std::cin), std::istream_iterator<int>(), std::back_inserter(v)); 
     std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " ")); 
    } 
    catch (const std::exception& e) { 
     std::cout << e.what() << '\n'; 
    } 
} 
+0

尼斯的答案。 +1但是...结束 2009-11-01 00:13:23

+0

对于像'istream_iterator'这样的一些迭代器,在末尾时递增。其他人,像指针一样,无限期地继续前进。处理这种情况的另一种方法是提前'run_length-1'并确保这不是结束,然后再去一次。 – 2009-11-01 04:35:50

+0

这完全取决于实施。随着VC++增量超越'std :: istream_iterator ()',会触发检查迭代器的错误,并且将向量迭代器推进到end()之外。没有算法应该超越最终迭代器。另外,'it + run_length - 1'具有不超过'it + run_length'的可能性。 – UncleBens 2009-11-01 11:31:35