LINUX.ORG.RU

История изменений

Исправление quasimoto, (текущая версия) :

Помимо STL-like итераторов есть ещё ranges — в их случае предполагается возвращать из контейнера не пару begin/end, а просто некий range реализующий нужный интерфейс ranges, внутри может быть как begin/end, так и вообще что угодно, интерфейс — от минимального bool empty() / operator* / operator= и далее forward / backward в виде operator++/operator-- и т.п. Плюс, к более общему интерфейсу, — возможность писать find(cont.all(), element) вместо find(cont.begin(), cont.end(), element), более удобные композиционные свойства, как следствие.

Пример с итерацией по 1D массиву кусками с последующими итерациями уже по кускам:

template <typename T>
struct Range {
    virtual ~Range() {}
    virtual bool empty() const = 0;
    virtual const T& operator*() const = 0;
};

template <typename T>
struct ForwardRange : Range<T> {
    virtual ~ForwardRange() {}
    virtual void operator++() = 0;
};

template<typename T, typename AForwardRange>
AForwardRange find(AForwardRange range, T value)
{
    for (; !range.empty(); ++range)
        if (value == *range)
            break;
    return range;
}

template<typename T, typename AForwardRange, typename Functor>
T fold(AForwardRange range, Functor fn)
{
    T res {};
    for (; !range.empty(); ++range)
        res = fn(res, *range);
    return res;
}

template<typename T, typename AForwardRange>
T sum(AForwardRange range)
{
    return fold<T>(range, std::plus<T>());
}

template <typename AForwardRange>
struct SliceForwardRange : ForwardRange<AForwardRange> {
    virtual ~SliceForwardRange() {}
    virtual AForwardRange slice() const = 0;
};

template<typename T, typename ASliceForwardRange, typename AForwardRange, typename Functor>
T async_fold(AForwardRange range, Functor fn, size_t n_steps)
{
    std::vector<std::future<T> > jobs;

    // map/fork
    for (ASliceForwardRange slices(range, n_steps); !slices.empty(); ++slices)
        jobs.push_back(std::async(std::launch::async, [=]() {
            T res {};
            for (auto sub_range = slices.slice(); !sub_range.empty(); ++sub_range)
                res = fn(res, *sub_range);
            return res;
        }));

    // reduce/join
    T res {};
    for (auto &job : jobs)
        res = fn(res, job.get());
    return res;
}

/* In 1D case. */

template <typename T>
struct MemoryForwardRange : public ForwardRange<T> {

    MemoryForwardRange(T *begin_, const T *end_) :
        begin(begin_), end(end_) {}

    bool empty() const { return begin >= end; }

    void operator++() { assert(!empty()); ++begin; }

    const T& operator*() const { assert(!empty()); return *begin; }

    T *begin;
    const T *end;

};

template <typename T>
struct MemorySliceForwardRange : public SliceForwardRange<MemoryForwardRange<T> > {

    MemorySliceForwardRange(const MemoryForwardRange<T> &range_, size_t n_steps_) :
        range(range_), n_steps(n_steps_) {}

    bool empty() const { return range.empty(); }

    void operator++() { assert(!empty()); range.begin += n_steps; }

    const MemoryForwardRange<T>& operator*() const { return range; }

    MemoryForwardRange<T> slice() const {
        assert(!empty());
        return
            range.begin + n_steps >= range.end ?
            MemoryForwardRange<T>(range.begin, range.end) :
            MemoryForwardRange<T>(range.begin, range.begin + n_steps);
    }

    MemoryForwardRange<T> range;
    const std::size_t n_steps;

};

// async_fold<T, MemorySliceForwardRange<T> >(
//     MemoryForwardRange<T>(xs, xs + n), std::plus<T>(), n / threads
// );

Исходная версия quasimoto, :

Помимо STL-like итераторов есть ещё ranges — в их случае предполагается возвращать из контейнера не пару begin/end, а просто некий range реализующий нужный интерфейс ranges, внутри может быть как begin/end, так и вообще что угодно, интерфейс — от минимального bool empty() / operator* / operator= и далее forward / backward в виде operator++/operator-- и т.п. Плюс, к более общему интерфейсу, — возможность писать find(cont.all(), element) вместо find(cont.begin(), cont.end(), element), более удобные композиционные свойства, как следствие.

Пример с итерацией по 1D массиву кусками с последующими итерациями уже по кускам:

template <typename T>
struct Range {
    virtual ~Range() {}
    virtual bool empty() const = 0;
    virtual const T& operator*() const = 0;
};

template <typename T>
struct ForwardRange : Range<T> {
    virtual ~ForwardRange() {}
    virtual void operator++() = 0;
};

template<typename T, typename AForwardRange>
AForwardRange find(AForwardRange range, T value)
{
    for (; !range.empty(); ++range)
        if (value == *range)
            break;
    return range;
}

template<typename T, typename AForwardRange, typename Functor>
T fold(AForwardRange range, Functor fn)
{
    T res {};
    for (; !range.empty(); ++range)
        res = fn(res, *range);
    return res;
}

template<typename T, typename AForwardRange>
T sum(AForwardRange range)
{
    return fold<T>(range, std::plus<T>());
}

template <typename AForwardRange>
struct SliceForwardRange : ForwardRange<AForwardRange> {
    virtual ~SliceForwardRange() {}
    virtual AForwardRange slice() const = 0;
};

template<typename T, typename ASliceForwardRange, typename AForwardRange, typename Functor>
T async_fold(AForwardRange range, Functor fn, size_t n_steps)
{
    std::vector<std::future<T> > jobs;

    // map/fork
    for (ASliceForwardRange slices(range, n_steps); !slices.empty(); ++slices)
        jobs.push_back(std::async(std::launch::async, [=]() {
            T res {};
            for (auto sub_range = slices.slice(); !sub_range.empty(); ++sub_range)
                res = fn(res, *sub_range);
            return res;
        }));

    // reduce/join
    T res {};
    for (auto &job : jobs)
        res = fn(res, job.get());
    return res;
}

/* In 1D case. */

template <typename T>
struct MemoryForwardRange : public ForwardRange<T> {

    MemoryForwardRange(T *begin_, const T *end_) :
        begin(begin_), end(end_) {}

    bool empty() const { return begin >= end; }

    void operator++() { assert(!empty()); ++begin; }

    const T& operator*() const { assert(!empty()); return *begin; }

    T *begin;
    const T *end;

};

template <typename T>
struct MemorySliceForwardRange : public SliceForwardRange<MemoryForwardRange<T> > {

    MemorySliceForwardRange(const MemoryForwardRange<T> &range_, size_t n_steps_) :
        begin(range_.begin), range(range_), n_steps(n_steps_) {}

    bool empty() const { return begin >= range.end; }

    void operator++() { assert(!empty()); begin += n_steps; }

    const MemoryForwardRange<T>& operator*() const { return range; }

    MemoryForwardRange<T> slice() const {
        assert(!empty());
        return
            begin + n_steps >= range.end ?
            MemoryForwardRange<T>(begin, range.end) :
            MemoryForwardRange<T>(begin, begin + n_steps);
    }

    T *begin;
    const MemoryForwardRange<T> range;
    const std::size_t n_steps;

};

// async_fold<T, MemorySliceForwardRange<T> >(
//     MemoryForwardRange<T>(xs, xs + n), std::plus<T>(), n / threads
// );