2012-10-22 119 views
6

我已经写了一个函数“富”在C,我想从R程序调用。该函数接受一个矩阵作为输入,并对其执行一些操作(比如说,将每个元素加1)。虽然很容易在单个矢量R .C接口:传递多维数组

.C("foo", n=as.integer(5), x=as.double(rnorm(5))) 

以foo实现为

void foo(int *nin, double *x) 
{ 
int n = nin[0]; 

int i; 

for (i=0; i<n; i++) 
    x[i] = x[i] * x[i]; 
} 

如何传递一个二维数组?如果我将“double * x”更改为“double ** x”,则会出现分段错误。任何指针赞赏。

回答

7

没有必要放弃在.C这样的直接操作。请记住,R中的矩阵是一个向量+维度。同样在C中,传递矩阵及其维数,并将矩阵的元素按照适当的偏移量存取到矢量中。像

void cplus1(double *x, int *dim) 
{ 
    for (int j = 0; j < dim[1]; ++j) 
     for (int i = 0; i < dim[0]; ++i) 
      x[j * dim[0] + i] += 1; 
} 

东西,所以使用inline作为一个很好的聚会招

library(inline) 
sig <- signature(x="numeric", dim="integer") 
body <- " 
    for (int j = 0; j < dim[1]; ++j) 
     for (int i = 0; i < dim[0]; ++i) 
      x[j * dim[0] + i] += 1; 
" 

cfun <- cfunction(sig, body=body, language="C", convention=".C") 

plus1 <- function(m) { 
    m[] = cfun(as.numeric(m), dim(m))$x 
    m 
} 
+0

'.C'增加了复制参数去C程序的开销时间,然后复制回结果,而'.Call'不 – transang

+0

@transang如果目标是_modify_从传递的对象R,那么你必须用C复制它;您可以使用'.C()'自动安全地复制副本,或者使用'.Call()'手动执行并且发生错误的概率很高。 Rcpp方式也需要一个内存拷贝,在Dirk的回答中是'Rcpp :: clone(M)'语句。当然,Rcpp对于严肃的C级开发来说太棒了。 –

8

放弃.C()并切换到.Call(),它允许您将所有R对象作为所谓的SEXP对象传递。

你可以通过R的C API或通过Rcpp(我们认为的)更好的更高级别的抽象来解析这些困难的方式。

R> library(inline) # use version 0.3.10 for rcpp() wrapper 
R> 
R> addMat <- rcpp(signature(ms="numeric"), body=' 
+ Rcpp::NumericMatrix M(ms); 
+ Rcpp::NumericMatrix N = Rcpp::clone(M); 
+ for (int i=0; i<M.nrow(); i++) 
+  for (int j=0; j<M.ncol(); j++) 
+   N(i,j) = M(i,j) + 1; 
+ return(N); 
+ ') 
R> addMat(matrix(1:9,3,3)) 
    [,1] [,2] [,3] 
[1,] 2 5 8 
[2,] 3 6 9 
[3,] 4 7 10 
R> 
0

随着.C,您必须通过额外的参数传递矩阵尺寸为@马丁摩根的答案。

随着.Call

SEXP foo(SEXP mat){ 
    SEXP dims = getAttttrib(mat, R_DimSymbol); 
    int nrow = INTEGER(dims)[0]; 
    int ncol = INTEGER(dims)[1]; 
    //... 
} 
+0

不要忘记复制该矩阵! –