首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >方便函数包装C++17's std::distance=1案例示例

方便函数包装C++17's std::distance=1案例示例
EN

Code Review用户
提问于 2021-05-20 18:07:53
回答 2查看 440关注 0票数 7

C++有一个函数性病:样本,它从一个范围随机采样并通过输出迭代器放置结果。我的目标是创建一个实用程序,在我们只想从容器中取样单个随机元素的事件中直接返回单个值。

代码语言:javascript
运行
复制
#include <iostream>
#include <random>
#include <algorithm>
#include <iterator>
#include <unordered_set>
#include <array>

template<class InputIt, class URBG>
auto singleRandomSample(InputIt t_begin, InputIt t_end, URBG&& rng)
{
    using value_type = typename std::iterator_traits<InputIt>::value_type;
    std::array<value_type, 1> out;
    std::sample(t_begin, t_end, out.begin(), 1, rng);
    return out.at(0);
}


int main()
{
    std::default_random_engine rng{ std::random_device{}() };
    std::unordered_set<char> vowels{ 'a', 'e', 'i', 'o', 'u', 'y' };
    for (std::size_t i = 0; i < 10; ++i)
    {
        auto randomVowel = singleRandomSample(vowels.begin(), vowels.end(), rng);
        std::cout << "Random vowel: " << randomVowel << '\n';
    }
}

此函数创建一个临时容器out,其中添加一个元素,然后按值返回该元素。它使用std::array,因为我知道我只需要一个元素的空间,尽管如果我决定能够在非默认的可构造类型中使用它很重要,我可以切换到一个std::vector和一个std::back_inserter

直接使用std::sample作为参数调用rng是正确的,还是应该使用std::forward(rng)

仅仅为了存储一个立即返回的值,整个临时容器就显得有点笨重。还有其他方法可以实现这个功能吗?

EN

回答 2

Code Review用户

回答已采纳

发布于 2021-05-20 20:16:00

指针是迭代器。

代码语言:javascript
运行
复制
template <typename InputIterator, typename URBG>
auto singleRandomSample(InputIterator first, InputIterator last, URBG&& rng)
{
    using value_type = typename std::iterator_traits<InputIterator>::value_type;

    auto result = value_type{};

    std::sample(first, last, &result, 1, std::forward<URBG>(rng));

    return result;
}

或者,使用更好的C++20接口:

代码语言:javascript
运行
复制
template <std::input_iterator I, std::sentinel_for<I> S, typename URBG>
    requires std::uniform_random_bit_generator<std::remove_reference_t<URBG>>
auto singleRandomSample(I first, S last, URBG&& rng)
{
    auto result = std::iter_value_t<I>{};

    std::ranges::sample(first, last, &result, 1, std::forward<URBG>(rng));

    return result;
}

template <std::input_range R, typename URBG>
    requires std::uniform_random_bit_generator<std::remove_reference_t<URBG>>
auto singleRandomSample(R&& r, URBG&& rng)
{
    auto result = std::ranges::range_value_t<R>{};

    std::ranges::sample(r, &result, 1, std::forward<URBG>(rng));

    return result;
}

这要求范围的值类型是默认的-可构造的,但是您已经依赖于数组解决方案了。

如果您想要一个不需要默认构造的解决方案,您很可能会使用一个可选的类型,比如std::optional,或者使用std::variantstd::monostate-and,这是一个定制的输出迭代器,可以对其进行嵌入。

票数 4
EN

Code Review用户

发布于 2021-05-20 19:54:50

接口

首先,我会考虑用例。如果需要从单通道序列(从文件或网络中流)进行采样,那么我将保留当前返回所选值的接口。这可能会打开一罐蠕虫。

否则,我将收紧迭代器的要求,以转发迭代器并返回该迭代器。这种接口将不处理非X可构造类型(其中X是一个或多个默认、复制和移动)。

我将把这一职能命名如下:

  • sample_single_value (在第一个接口情况下)
  • select_uniformly (第二接口)
  • pick_uniformly (第二个接口,尽管不太喜欢这个)

Implementation

在对libstdc++和libc++进行校验后,两者似乎都实现了储层采样,这对于第二个接口来说并不难手工实现。对于第一个接口,当前的实现看起来很好(手工编写更容易出错,可读性更低)。

随机访问迭代器情况下的

优化

在已知人口规模的情况下,人们可能会倾向于进行优化,但是由于RNG源不可重入的性质,由于RNG调用次数的不同,这种优化很可能会产生不同的结果。我不会建议实现优化,或者至少我会在文档中把这个奇怪的字体设置为大的、粗体的、红色的字体。

票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/260995

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档