【问题标题】:How to write a range-v3 action for random_shuffle?如何为 random_shuffle 编写 range-v3 动作?
【发布时间】:2015-08-06 04:07:15
【问题描述】:

使用range-v3 library(@EricNiebler),使得编写算法代码更加紧凑,例如下面是如何生成一堆随机数:

#include <range/v3/all.hpp>
#include <iostream>
#include <vector>

int main() 
{
    using namespace ranges;

    auto const N = 10;
    std::vector<int> v; 
    v.reserve(N);

    v |= action::push_back(view::iota(0, N)); 
    random_shuffle(v);
    copy(v, ostream_iterator<>(std::cout, ","));
}

Live Example.

但是,我更愿意像这样使用假设的action::random_shuffle() 来扩展管道

v |= action::push_back(view::iota(0, N)) | action::random_shuffle();

这是我编写这样一个动作的尝试(不幸的是,编写新的 range-v3 代码比使用库要冗长一些)

#include <functional> // bind, placeholders::_1

namespace ranges
{
    inline namespace v3
    {
        /// \addtogroup group-actions
        /// @{
        namespace action
        {
            struct random_shuffle_fn
            {
            private:
                friend action_access;

                static auto bind(random_shuffle_fn random_shuffle)
                RANGES_DECLTYPE_AUTO_RETURN
                (
                    std::bind(random_shuffle, std::placeholders::_1)
                )

                template<typename Gen>
                static auto bind(random_shuffle_fn random_shuffle, Gen && rand)
                RANGES_DECLTYPE_AUTO_RETURN
                (
                    std::bind(random_shuffle, std::placeholders::_1, bind_forward<Gen>(rand))
                )
            public:
                struct ConceptImpl
                {
                    template<typename Rng,
                        typename I = range_iterator_t<Rng>>
                    auto requires_(Rng&&) -> decltype(
                        concepts::valid_expr(
                            concepts::model_of<concepts::RandomAccessRange, Rng>(),
                            concepts::is_true(Permutable<I>())
                        ));
                };

                template<typename Rng>
                using Concept = concepts::models<ConceptImpl, Rng>;

                template<typename Rng,
                    CONCEPT_REQUIRES_(Concept<Rng>())>
                Rng operator()(Rng && rng) const
                {
                    ranges::random_shuffle(rng);
                    return std::forward<Rng>(rng);
                }

                template<typename Rng, typename Gen,
                    CONCEPT_REQUIRES_(Concept<Rng>())>
                Rng operator()(Rng && rng, Gen && rand) const
                {
                    ranges::random_shuffle(rng, std::forward<Gen>(rand));
                    return std::forward<Rng>(rng);
                }

                #ifndef RANGES_DOXYGEN_INVOKED
                template<typename Rng>
                void operator()(Rng &&) const
                {
                    CONCEPT_ASSERT_MSG(RandomAccessRange<Rng>(),
                        "The object on which action::random_shuffle operates must be a model of the "
                        "RandomAccessRange concept.");
                    using I = range_iterator_t<Rng>;
                    CONCEPT_ASSERT_MSG(Permutable<I>(),
                        "The iterator type of the range passed to action::random_shuffle must allow its "
                        "elements to be permuted; that is, the values must be movable and the "
                        "iterator must be mutable.");
                }
            #endif
            };

            /// \ingroup group-actions
            /// \relates sort_fn
            /// \sa `action`
            namespace
            {
                constexpr auto&& random_shuffle = static_const<action<random_shuffle_fn>>::value;
            }
        }
        /// @}
    }
}

Live Example 编译失败,因为一些operator() 深藏在某处找不到。

据我所知,我忠实地从类似代码中翻译了上述代码,例如action::sort()。唯一的区别是random_shuffle() 有两个重载(一个采用随机生成器),而所有其他操作(包括sort)都有一个重载,其额外参数(比较器、谓词、投影仪等)具有默认值.)。这转化为上面random_shuffle_fn 的两个bind() 静态成员函数,而所有其他操作只有一个bind() 重载。

问题:如何为 random_shuffle 编写 range-v3 动作?

【问题讨论】:

  • 与问题不完全相关,但:std::random_shuffle 已弃用。使用std::shuffle
  • @Casey 会不会把iota 打电话给reserve
  • 好吧,如果std::vector&lt;int&gt;{view::iota(0, N)} 有效,我会这样做。抱歉,我一直在实现 range-v3-aware 容器,所以我已经习惯了这种语法;)

标签: c++ algorithm c++14 range-v3


【解决方案1】:

您有两个模棱两可的 random_shuffle_function::operator()(Rng&amp;&amp;) 重载,您的“错误捕获”重载需要被限制为仅接受适当重载拒绝的那些参数(我们真的需要 C++ 概念,所以我再也不会必须 SFINAE 约束重载):

#ifndef RANGES_DOXYGEN_INVOKED
template<typename Rng,
    CONCEPT_REQUIRES_(!Concept<Rng>())>
void operator()(Rng &&) const
{
    CONCEPT_ASSERT_MSG(RandomAccessRange<Rng>(),
        "The object on which action::random_shuffle operates must be a model of the "
        "RandomAccessRange concept.");
    using I = range_iterator_t<Rng>;
    CONCEPT_ASSERT_MSG(Permutable<I>(),
        "The iterator type of the range passed to action::random_shuffle must allow its "
        "elements to be permuted; that is, the values must be movable and the "
        "iterator must be mutable.");
}
#endif

另外,你需要通过action::random_shuffle

v |= action::push_back(view::iota(0, N)) | action::random_shuffle;

DEMO

【讨论】:

  • 太好了,感谢您使用它。缺少的 CONCEPTS_REQUIRE 纯属疏忽,因为我从action::sort 代码中复制了一些额外的模板参数。我不知道为什么我把括号放在action::random_shuffle 上,也许是因为我希望它是一个空函数对象。那么这里到底叫什么?
  • 另一条评论:请考虑将此作为对 Github 的拉取请求!世界需要更多行动:)
  • 我很乐意接受action::shuffle 的拉取请求。不适用于action::random_shuffle
【解决方案2】:

git 的最新版本已经包含action::shuffle。可以这样使用:

#include <random>
std::mt19937 gen;
...
v |= action::push_back(view::iota(0, N)) | action::shuffle(gen);

【讨论】:

  • Tnx,我知道,测试过 :)
猜你喜欢
  • 1970-01-01
  • 2020-07-26
  • 2019-07-09
  • 2019-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-09
相关资源
最近更新 更多