我的问题比这更复杂,所以我把它缩小到一个非常简单的例子,它会告诉我如何处理剩下的事情。
说我有一个输入迭代器。我想创建一个从它派生的新输入迭代器,其中每个元素是原始输入的多个顺序元素与以下模式的组合。运行长度在输入序列中编码。
输入:
{ 1 1 2 3 4 4 6 7 8 9 ... }
输出:
{ (1) (3+4) (6+7+8+9) ... }
我在想这样的函数可以处理单个元素并递增输入开始迭代器(通过引用传递)。我的评论中有几个问题,而且我想知道是否有一个很好的方法可以为整个元素流做到这一点。
编辑:我知道调用std::advance
时出现错误,其中tmp
迭代器增加到end
,这将是对此代码有效。让我们专注于我的其余问题,我会解决这个问题。 编辑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);
}
}
答案 0 :(得分: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';
}
}
答案 1 :(得分: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.");
这里的问题是&lt;运算符,它只适用于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的好类型。呼叫者经常希望在调用您的函数后保留它们。例如,传递byRef会导致无法编译:
std::vector<something> t;
std::vector<something> t2;
process_single(t.begin(), t.end(), std::back_inserter(t2))
(许多编译器会接受它,但它不是标准的)
最好是传递迭代器byval,然后返回结束算法的新位置,以便与STL的其余部分更加一致。例如,请参阅std :: find()。
希望有所帮助....