函数.C -- 参数设置导致编译失败

4
我目前正在学习如何在R中调用已编译的C代码。昨天我创建了一个无限π级数的函数,当在R中运行时返回长度为1的数值向量(pi),非常有效。今天我正在编写一个输出变长数值向量的函数,即用户定义的Fibonacci序列。
在《Writing R Extensions》中指出,“编译的代码不应该返回任何东西,除非通过它的参数”。这就是我遇到困难的地方。我不知道如何设置以下C代码的最终“else”语句中的参数,以便在调用“system”后不会触发错误。
我在C程序中编译了一个稍微不同的版本,它可以正常工作。然而,为了符合R函数“.C”的规格,设置方式有所不同。
我该如何更改以下函数,以使其可以编译且没有警告或错误,并且可以在R函数“.C”中调用?
要编译的C代码:
void fibonacci(int *n, int *ans)
{
  # *ans = 0;  I tried this, didn't work

  if(*n == 1 || *n == 2){
    *ans = 1;
  } else if(*n == 0){
    *ans = 0;
  } else {
    *ans = fibonacci(n - 2) + fibonacci(n - 1); # tried 'ans' in arguments
  }                                             # here, didn't work
}

错误发生在这里:

## compile and create shared library
> system('gcc -Wall -g -c -fPIC fibonacciR.c -o fibonacciR.o')
fibonacciR.c: In function ‘fibonacci’:
fibonacciR.c:8:5: error: too few arguments to function ‘fibonacci’
fibonacciR.c:1:6: note: declared here
fibonacciR.c:8:5: error: too few arguments to function ‘fibonacci’
fibonacciR.c:1:6: note: declared here

将C代码中的最终ans行更改为:

} else {
  *ans = fibonacci(n - 2, ans) + fibonacci(n - 1, ans);
}

生成了不同的错误。
> system('gcc -Wall -g -c -fPIC fibonacciR.c -o fibonacciR.o')
fibonacciR.c: In function ‘fibonacci’:
fibonacciR.c:10:5: error: void value not ignored as it ought to be
fibonacciR.c:10:5: error: void value not ignored as it ought to be

我绝对不是一个专业的C程序员,也不知道如何忽略一个参数。此外,这个函数的设置方式,在R中调用它时只会返回一个值。所以我认为我需要为用户定义的参数n循环使用.C。这是正确的吗?


一旦错误得到解决,将要执行的调用如下。

system('gcc -Wall -g fibonacciR.o -shared -o libfibonacciR.so')
## load compiled program into R
dyn.load('libfibonacciR.so')
## create function to call compiled program
fibonacci <- function(n)
{
  .C('fibonacci', as.integer(n), ans = as.integer(ans))$ans
}
## call function - result should be first 10 fibonacci numbers
fibonacci(n = 10)
## expected result
[1]  1 1 2 3 5 8 13 21 34 55

1
fibonacci() 函数需要 2 个参数。你只传递了一个(第一个)。尝试使用 fibonacci(n - 2, ans) + fibonacci(n - 1, ans)。 - pah
好的,我试过了。在同一行的注释中有说明。 - Rich Scriven
1
错误还是完全一样的吗? - pah
请注意,fibonacci() 不会返回一个值,而您正在操作它的返回值。在您解决缺少参数问题后,这将导致另一个编译器错误。 - pah
是的,我应该删除 c 标签。通过 R 进行编译的设置有所不同。对此感到抱歉。 - Rich Scriven
1
顺便提一下,你不应该使用.C接口,因为它已经过时并且不建议使用。相反,使用.Call()。另外,也许你不知道有一个内联包?你可能会发现http://adv-r.had.co.nz/C-interface.html很有帮助。 - hadley
3个回答

2

您已经知道以下行中的错误。

*ans = fibonacci(n - 2) + fibonacci(n - 1); # tried 'ans' in args here,

我认为你应该编写一个帮助函数,它可以完成所有工作,同时简化了整个过程。
int fibonacci_helper(n)
{
  if(n == 1 || n == 2){
    return 1;
  } else if(n == 0){
    return 0;
  } else {
    return fibonacci_helper(n-1) + fibonacci_helper(n-2);
  }
}

void fibonacci(int *n, int *ans)
{
  *ans = fibonacci_helper(*n);
}

2
你可以加载Rcpp来获取其Rcpp Attributes功能。它允许你将代码编写为以下形式(为了显示而展开成两行,实际上只需要一行即可):
R> cppFunction('double fib(double n) { if (n<2) return(n); 
+                                      else return fib(n-1) + fib(n-2);}')
R> fib(10)
[1] 55
R> 

如果你运行cppFunction(..., verbose=TRUE),你可以看到它创建的文件。

一般来说,避免使用.C()接口,而集中使用.Call()。这是最近R发布版NEWS文件中非常明确的建议:

* .C(DUP = FALSE) and .Fortran(DUP = FALSE) are now deprecated, and
  may be disabled in future versions of R.  As their help has long
  said, .Call() is much preferred.

让我们再次强调.Call()更受欢迎。请参见Hadley的高级R编程草稿,了解有关使用.Call()的更多信息。他的建议也是直接使用Rcpp。


哇,cppFunction 可能会解决我所有的问题。:) 非常感谢。我有一份印刷版的《编写 R 扩展》手册,日期是 2013 年 9 月 25 日,猜想我忘记查看新闻发布了。干杯! - Rich Scriven
做过“漫长而艰苦的方式”确实有所帮助,就像你所做的那样。既然已经完成了,现在可以享受一些提高生产力的工具 :) - Dirk Eddelbuettel

0

fibonacci被声明为接收两个指针作为参数,并且不返回任何内容。但在您的else块中,您尝试使用单个整数参数调用它并捕获其返回值。

else块中尝试像这样操作:

int n1 = *n - 1;
int n2 = *n - 2;
int ans1;
int ans2;
fibonacci(&n1, &ans1);
fobonacci(&n2, &ans2);
*ans = ans1 + ans2;

此外,在您的else if块中,应该是(*n == 0)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接