转换多个迭代器元素

发布于 2024-08-09 18:57:27 字数 2331 浏览 2 评论 0原文

我的问题比这更复杂,所以我将其范围缩小到一个非常简单的示例,该示例足以让我知道如何处理其余的问题。

假设我有一个输入迭代器。我想从它派生一个新的输入迭代器,其中每个元素都是原始输入的多个顺序元素与以下模式的组合。游程长度被编码在输入序列中。

输入: { 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);
    }
}

My problem is more complex than this, so I've narrowed it down to a very simple example that would show me enough to know how to handle the rest.

Say I have an input iterator. I want make a new input iterator derived from it, where each element is the combination of multiple sequential elements of the original input with the following pattern. The run length is encoded in the input sequence.

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

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

I was thinking a function like this could process a single element and increment the input begin iterator (passed by reference). There are a few questions in my comments, plus I'd like to know if there's a good way to do it for the entire stream of elements.

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. Edit 2: should be fixed now?

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

Edit 3: In response to the answers, would this be better?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

半窗疏影 2024-08-16 18:57:27

我会手动编写这个算法。

首先,该函数不接受输入迭代器,因为它们不支持前进和距离。

其次,错误检查已关闭。如果我没记错的话, end <的可能性tmp 表示已调用一些未定义的行为。想象容器是一个 std::list。如果你设法超越 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';
    }
}

I would write this algorithm manually.

Firstly, the function does not accept an input iterator, because those don't support advance and distance.

Secondly, the error checking is off. If I'm not mistaken, the possibility of end < tmp means some undefined behaviour has been invoked. Imagine the container is a std::list. What would happen if you managed to advance beyong list.end()? But I think it would be undefined even with a vector or array (and MSVC++ would probably kick in with its iterator debugging before you).

So, to decode the whole sequence, I'd do something like this:

#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';
    }
}
匿名的好友 2024-08-16 18:57:27
// 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 的好类型。调用者常常希望在调用函数后保留它们。例如,传递 byRef 会导致它无法编译:(

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

许多编译器会接受它,但它不是标准的)

更好的方法是传递迭代器 byval ,然后返回结束算法的新位置,以便与其余部分更加一致STL 的。例如,请参见 std::find()。

希望有帮助......

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

Not really.

// 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.");

The problem here is the < operator, which is only going to work for RandomAccessIterators. Why not just:

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

?

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

Nope.

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. 

Incrementing to end is standard behavior for STL algorithms.

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

STL iterators aren't generally a good type to pass byref. Callers all too often want to preserve them after the call to your function. For example, passing byRef causes this not to compile:

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

(Many compilers will take it but it's not standard)

Better would be to pass the iterator byval and then return the new position at which you end your algorithm, to be more consistent with the rest of the STL. For example, see std::find().

Hope that helps....

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文