使用 Rcpp 属性允许 C++ 常量成为默认函数参数

Allow C++ constants to be a default function parameter using Rcpp Attributes

我在 R 包中使用 rcpp 创建了一个 cumsum 函数,它将对一个向量进行累积求和,直到它达到用户定义的上限或下限。但是,如果要将 cumsum 限制在上方,用户仍必须指定一个底限。

示例:

a = c(1, 1, 1, 1, 1, 1, 1)

如果我想累加 a 并且上限为 3,我可以 cumsum_bounded(a, lower = 1, upper = 3)。我宁愿不必指定下限。

我的代码:

#include <Rcpp.h>
#include <float.h>
#include <cmath>
using namespace Rcpp;

// [[Rcpp::export]]
NumericVector cumsum_bounded(NumericVector x, int upper, int lower) {
  NumericVector res(x.size());
  double acc = 0;
  for (int i=0; i < x.size(); ++i) {
    acc += x[i];
    if (acc < lower)  acc = lower;
    else if (acc > upper)  acc = upper;
    res[i] = acc;
  }
  return res;
}

我想要什么:

#include <Rcpp.h>
#include <float.h>
#include <cmath>
#include <climits> //for LLONG_MIN and LLONG_MAX
using namespace Rcpp;

// [[Rcpp::export]]
NumericVector cumsum_bounded(NumericVector x, long long int upper = LLONG_MAX, long long int lower = LLONG_MIN) {
  NumericVector res(x.size());
  double acc = 0;
  for (int i=0; i < x.size(); ++i) {
    acc += x[i];
    if (acc < lower)  acc = lower;
    else if (acc > upper)  acc = upper;
    res[i] = acc;
  }
  return res;
}

简而言之,是的,这是可能的,但它需要技巧,涉及创建中间函数或在主函数中嵌入排序逻辑。


总之,Rcpp 属性只支持限制特征值集。这些值列在 Rcpp FAQ 3.12 entry

  • String literals delimited by quotes (e.g. "foo")
  • Integer and Decimal numeric values (e.g. 10 or 4.5)
  • Pre-defined constants including:
    • Booleans: true and false
    • Null Values: R_NilValue, NA_STRING, NA_INTEGER, NA_REAL, and NA_LOGICAL.
  • Selected vector types can be instantiated using the empty form of the ::create static member function.
    • CharacterVector, IntegerVector, and NumericVector
  • Matrix types instantiated using the rows, cols constructor Rcpp::Matrix n(rows,cols)
    • CharacterMatrix, IntegerMatrix, and NumericMatrix)

如果您要为 LLONG_MAXLLONG_MIN 指定数值,这将满足直接在函数上使用 Rcpp 属性的条件。但是,这些值是特定于实现的。因此,对它们进行硬编码并不理想。因此,我们必须寻求外部解决方案: Rcpp::Nullable<T> class 启用默认值 NULL 。之所以要把参数类型用Rcpp::Nullable<T>包裹起来,是因为NULL很特殊,一不小心就会心痛

NULL 值与实数线上的其他值不同,在这种情况下不会用于限制您的值。因此,它是用于函数调用的完美候选者。然后您必须做出两个选择:使用 Rcpp::Nullable<T> 作为主函数的参数,或者创建一个 "logic" 辅助函数,它具有正确的参数并且可以在您的应用程序中的其他地方使用而无需担心。我选择了下面的后者。

#include <Rcpp.h>
#include <float.h>
#include <cmath>
#include <climits> //for LLONG_MIN and LLONG_MAX
using namespace Rcpp;

NumericVector cumsum_bounded_logic(NumericVector x,
                                   long long int upper = LLONG_MAX,
                                   long long int lower = LLONG_MIN) {

    NumericVector res(x.size());
    double acc = 0;
    for (int i=0; i < x.size(); ++i) {
        acc += x[i];
        if (acc < lower)  acc = lower;
        else if (acc > upper)  acc = upper;
        res[i] = acc;
    }

    return res;
}

// [[Rcpp::export]]
NumericVector cumsum_bounded(NumericVector x,
                             Rcpp::Nullable<long long int> upper = R_NilValue, 
                             Rcpp::Nullable<long long int> lower = R_NilValue) {

    if(upper.isNotNull() && lower.isNotNull()){
        return cumsum_bounded_logic(x, Rcpp::as< long long int >(upper), Rcpp::as< long long int >(lower));
    } else if(upper.isNull() && lower.isNotNull()){
        return cumsum_bounded_logic(x, LLONG_MAX, Rcpp::as< long long int >(lower));
    } else if(upper.isNotNull() && lower.isNull()) {
        return cumsum_bounded_logic(x, Rcpp::as< long long int >(upper), LLONG_MIN);
    } else {
        return cumsum_bounded_logic(x, LLONG_MAX, LLONG_MIN);
    }

    // Required to quiet compiler
    return x;
}

测试输出

cumsum_bounded(a, 5)
## [1] 1 2 3 4 5 5 5
cumsum_bounded(a, 5, 2)
## [1] 2 3 4 5 5 5 5