Rcpp 按引用传递与按值传递

标签 r rcpp

我通过 inline 第一次尝试了 Rcpp 函数它解决了我的速度问题(感谢 Dirk!):
Replace negative values by zero
最初的版本是这样的:

library(inline)
cpp_if_src <- '
  Rcpp::NumericVector xa(a);
  int n_xa = xa.size();
  for(int i=0; i < n_xa; i++) {
    if(xa[i]<0) xa[i] = 0;
  }
  return xa;
'
cpp_if <- cxxfunction(signature(a="numeric"), cpp_if_src, plugin="Rcpp")
但是当被调用时cpp_if(p) ,它覆盖了 p与输出,这不是预期的。所以我认为它是通过引用传递的。
所以我用以下版本修复了它:
library(inline)
cpp_if_src <- '
  Rcpp::NumericVector xa(a);
  int n_xa = xa.size();
  Rcpp::NumericVector xr(a);
  for(int i=0; i < n_xa; i++) {
    if(xr[i]<0) xr[i] = 0;
  }
  return xr;
'
cpp_if <- cxxfunction(signature(a="numeric"), cpp_if_src, plugin="Rcpp")
这似乎有效。但是现在,当我将其重新加载到 R 中时,原始版本不再覆盖其输入(即,现在相同的代码不会覆盖其输入):
> cpp_if_src <- '
+   Rcpp::NumericVector xa(a);
+   int n_xa = xa.size();
+   for(int i=0; i < n_xa; i++) {
+     if(xa[i]<0) xa[i] = 0;
+   }
+   return xa;
+ '
> cpp_if <- cxxfunction(signature(a="numeric"), cpp_if_src, plugin="Rcpp")
> 
> p
 [1] -5 -4 -3 -2 -1  0  1  2  3  4  5
> cpp_if(p)
 [1] 0 0 0 0 0 0 1 2 3 4 5
> p
 [1] -5 -4 -3 -2 -1  0  1  2  3  4  5
我不是唯一一个试图复制这种行为并发现不一致结果的人:
https://chat.stackoverflow.com/transcript/message/4357344#4357344
这里发生了什么?

最佳答案

他们的关键是“代理模型”——您的 xa确实与原始对象具有相同的内存位置,因此您最终会更改原始对象。

如果你不想那样做,你应该做一件事:使用 clone() 进行(深度)复制方法,或者可能显式创建一个新对象,将更改的对象写入其中。方法二不这样做,您只需使用两个不同命名的变量,它们都是原始变量的“指针”(在代理模型意义上)。

但是,当您将 int 向量(来自 R)传递给 NumericVector 类型时,另一个复杂情况是隐式强制转换和复制:这会创建一个副本,然后原件不再被更改。

这是一个更明确的示例,类似于我在教程或研讨会中使用的示例:

library(inline)
f1 <- cxxfunction(signature(a="numeric"), plugin="Rcpp", body='
  Rcpp::NumericVector xa(a);
  int n = xa.size();
  for(int i=0; i < n; i++) {
    if(xa[i]<0) xa[i] = 0;
  }
  return xa;
')

f2 <- cxxfunction(signature(a="numeric"), plugin="Rcpp", body='
  Rcpp::NumericVector xa(a);
  int n = xa.size();
  Rcpp::NumericVector xr(a);            // still points to a
  for(int i=0; i < n; i++) {
    if(xr[i]<0) xr[i] = 0;
  }
  return xr;
')

p <- seq(-2,2)
print(class(p))
print(cbind(f1(p), p))
print(cbind(f2(p), p))
p <- as.numeric(seq(-2,2))
print(class(p))
print(cbind(f1(p), p))
print(cbind(f2(p), p))

这就是我所看到的:
edd@max:~/svn/rcpp/pkg$ r /tmp/ari.r
Loading required package: methods
[1] "integer"
        p
[1,] 0 -2
[2,] 0 -1
[3,] 0  0
[4,] 1  1
[5,] 2  2
        p
[1,] 0 -2
[2,] 0 -1
[3,] 0  0
[4,] 1  1
[5,] 2  2
[1] "numeric"
       p
[1,] 0 0
[2,] 0 0
[3,] 0 0
[4,] 1 1
[5,] 2 2
       p
[1,] 0 0
[2,] 0 0
[3,] 0 0
[4,] 1 1
[5,] 2 2
edd@max:~/svn/rcpp/pkg$

因此,传递 int-to-float 或 float-to-float 真的很重要。

关于Rcpp 按引用传递与按值传递,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11300048/

相关文章:

eigen - RcppEigen LLT 产生 NaN 向量

r - dBWriteTable 无法将 NULL 写入 SQL Server 表

c++ - RcppArmadillo:arma::cube 的 vector

r - 如何解决此网页抓取问题

使用 dplyr 按行位置引用值

删除 MRO 3.2.3 后使用 ubuntu 14.04 和 R 3.2.3 进行 Rcpp 安装错误

c++ - 使用 Rcpp 制作包并尝试让一个类引用另一个类

c++ - 将现有的 C++ 代码移植到 R

r - 有效地创建变量,指示日期变量是否先于事件(按组)

r - 如何以编程方式提供要通过 dplyr 和 filter_ 应用的过滤器列表