首页 > 解决方案 > 如何设计一个带有可选随机种子参数的函数以传递给 mt19937

问题描述

在 RI 中可以构建以下roll_die(seed = NULL)函数,该函数返回 1 到 6 之间的随机整数,并且它允许为 RNG 指定种子的选项。

roll_die_r <- function(seed = NULL){
  # Returns a random number between 1 and 6
  # Optionally specify a RNG seed

  set.seed(seed)
  return(sample(x = 1:6, size = 1L))
}

这很好,因为我可以使用默认值调用它seed = NULL并返回一个随机值,或者我可以使用指定的种子值调用它,这样我就可以获得可重现的结果。

roll_die_r()  # random
roll_die_r(seed = 0)  # always returns 6

如何使用mt19937在 c++ 中实现相同的功能?我能想到的最好的是

#include <Rcpp.h>
#include <random>
using namespace Rcpp;

// [[Rcpp::plugins(cpp11)]]

// [[Rcpp::export]]
int roll_die_cpp(int seed = -1){
  // Returns a random integer between 1 and 6
  // Optionally specify a RNG seed

  std::mt19937 mt;

  // Seed the RNG
  if(seed == -1) seed = std::random_device{}();
  mt.seed(seed);

  std::uniform_int_distribution<int> dist(1, 6);
  int result = dist(mt);
  return result;
}

但这并不理想,因为用户可能会意外地call roll_die_cpp(seed = -1)期望得到可重现的结果,但事实并非如此。

roll_die_cpp()  # random
roll_die_cpp(seed = 0)  # always returns 5
roll_die_cpp(seed = -1)  # random

我的问题不是专门关于roll_die()方法或随机数生成器 - 它更多的是关于功能设计。在 RI 中经常使用默认参数设置为 NULL 的函数,但我不知道如何在 c++ 中完成同样的事情。

更新:这是我得到的另一个例子。

函数

return_0 <- function(msg = NULL){
  if(!is.null(msg)) print(msg)
  return(0L)
}
return_0()  # doesn't print a message
return_0("hello world")  # prints hello world

cpp函数

// [[Rcpp::export]]
int return_0_cpp(std::string msg = "don't print"){
  if(msg != "don't print") Rcpp::Rcout << msg;
  return(0);
}

return_0_cpp()  # doesn't print a message
return_0_cpp(msg = "hello world")  # prints hello world
return_0_cpp(msg = "don't print")  # doesn't print a message

注意有多尴尬return_0_cpp()。我在 R 中创建的 cpp 中的一种干净的方式是什么?

标签: c++rrcpp

解决方案


在 RI 中经常使用默认参数设置为 NULL 的函数,但我不知道如何在 c++ 中完成同样的事情。

std::optional(C++17 起) 用于可选值:

#include <iostream>
#include <optional>

void fun(std::optional<int> v = std::nullopt) {
    if (v) {
        std::cout << "value passed = " << v.value();
    } else {
        std::cout << "no value passed";
    }
}

int main(){ 
    fun();
    fun(4);
}

作为旁注:根据传递的参数数量,我会小心地让同一个函数做两件不同的事情。有人可能会说

dice.seed(0); 
auto x = dice.roll();

auto x = dice.roll(0);

推荐阅读