优雅(迭代)解析块中的数据?

时间:2015-09-03 17:01:08

标签: c++ yield

我们有一些“可迭代的”数据集合,例如:std::vector<Foo> bar。 我们希望处理来自Foo的{​​{1}}元素,直到满足某些条件,此时我们“屈服”(想想Python)所有这些处理过的bar的结果并等到下一个块请求解析的信息。

到目前为止,我们正在做的是:

Foo

有关更好地做到这一点的任何建议吗?

1 个答案:

答案 0 :(得分:2)

正如我在评论中已经指出的那样,IMO是自定义迭代器的一个非常好的例子:

迭代器扫描你的范围,只要一些谓词成立,并且 当谓词对于某个元素不成立时,调用具有谓词所持有的子元素范围的函数(加上不满足谓词的元素)。那么函数调用的结果就是你取消引用迭代器时得到的值。

template<typename Fn, typename Predicate, typename Iterator>
struct collector {
    using value_type = typename std::result_of<Fn(Iterator, Iterator)>::type;
    using pointer = value_type const *;
    using reference = value_type const &;
    using difference_type = std::ptrdiff_t;
    using iterator_category = std::forward_iterator_tag;

    value_type cache;
    Fn fn;
    Predicate predicate;
    Iterator pos, from, stop;


    collector(Fn&& fn, Predicate&& p, Iterator&& s, Iterator&& e)
     :  fn(std::forward<Fn>(fn)),
        predicate(std::forward<Predicate>(p)),
        pos(std::forward<Iterator>(s)),
        from(pos),
        stop(std::forward<Iterator>(e))
    {
        next_range();
    }

    collector & operator++(void) {
        next_range();
        return *this;
    }
    reference operator*(void) const {
        return cache;
    }

    void next_range(void) {
        from = pos;
        if (pos == stop) return;
        for (; pos != stop; ++pos) {
            if (not predicate(*pos)) {
                ++pos;
                break;
            }
        }
        cache = fn(from, pos);
    }

    collector end_of_range(void) const {
        auto copy = collector{*this};
        copy.pos = copy.stop;
        copy.from = copy.stop;
        return copy;
    }

    bool operator==(collector const & rhs) const {
        return (from == rhs.from) and (pos == rhs.pos) and (stop == rhs.stop);
    }
    bool operator!=(collector const & rhs) const {
        return (from != rhs.from) or (pos != rhs.pos) or (stop != rhs.stop);
    }

};

template<typename Fn, typename Predicate, typename Iterator>
auto make_collector_range(Fn&& fn, Predicate&& p, Iterator&& s, Iterator&& e) {
    using I = typename std::decay<Iterator>::type;
    using P = typename std::decay<Predicate>::type;
    using F = typename std::decay<Fn>::type;
    using C = collector<F,P,I>;
    auto start = C{
        std::forward<Fn>(fn), std::forward<Predicate>(p),
        std::forward<Iterator>(s), std::forward<Iterator>(e)};
    auto stop = start.end_of_range();
    return make_pair(std::move(start), std::move(stop));
}

一个示例用法,计算到50之前的数字之和,但不是一步,而是每个15个数字的步骤:

int main(int, char**) {
    vector<int> numbers = vector<int>(50);
    generate(begin(numbers), end(numbers),
        [i = 0] (void) mutable{
            return ++i;
        });
    copy(begin(numbers), end(numbers), ostream_iterator<int>{cout, " "});
    cout << endl;
    auto collected = make_collector_range(
        [](auto const & from, auto const & to) {
            return accumulate(from, to, 0);
        },
        [](auto const & num) {
            return not ((num % 3 == 0) and (num % 5 == 0));
        },
        begin(numbers), end(numbers));
    copy(collected.first, collected.second, ostream_iterator<int>{cout, " "});
    cout << endl;
    bool passed = accumulate(collected.first, collected.second, 0) == (50*51)/2;
    cout << "test " << (passed ? "passed" : "failed") << endl;
    return 0;
}

Live demo here

注意:此示例使用固定的“步长”,谓词和函数彼此无关且不保持状态,但迭代器不需要这样做。

我希望代码的意图是明确的,如果不是,我可以尝试提供有关其工作的更详细的解释。