4
votes

I'm currently learning to call compiled C code in R. Yesterday I created a function for the infinite pi series that when run in R returns a length 1 numeric vector (pi), which works great. Today I'm working on a function that outputs a variable length numeric vector, the Fibonacci sequence calculated for a user-defined n.

In Writing R Extensions, it is noted that "the compiled code should not return anything, except through it's arguments". This is where I'm running into trouble. I don't know how to set up the following C code, namely the arguments in the final else statement, so that no error is triggered following the call to system.

I compiled a slightly different version in a C program and it works. However, the setup is different for compiling code that is to be called in R. I had to adjust it to accommodate the specifications for R function .C.

How can I change the following function so that is compiles with no warnings or errors and can be called in R function .C?

C code to be compiled:

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
}

Error occurs here:

## 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

Changing the final ans line in the C code to

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

produces a different error

> 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

I'm definitely not an expert C programmer, and I do not know how to ignore an argument. Additionally, the way this function is set up, when I call it in R it will only return one value. So I presume I need to loop .C for the user-defined argument n. Is that correct?


Calls to follow, once I get the error sorted out.

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
3
The fibonacci() function takes 2 arguments. You're only passing one (the first). Try fibonacci(n - 2, ans) + fibonacci(n - 1, ans)pah
Right, I tried that. It's noted in the comment on the same line.Rich Scriven
And the error remained exactly the same?pah
Also note that fibonacci() doesn't return a value, and you're operating over it's return value. That will cause another compiler error after you fix the missing argument issue.pah
BTW, you shouldn't be using the .C interface, it is old and not recommended. Instead use .Call(). Also maybe you don't know about the inline package? You might find adv-r.had.co.nz/C-interface.html helpful.hadley

3 Answers

2
votes

You could load Rcpp just to get access to its Rcpp Attributes functionality. It permits you to write this as (expanded to two lines for display; it really fits on one line):

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> 

If you run cppFunction(..., verbose=TRUE) you get to see the file it creates.

In general, avoid the .C() interface and concentrate in .Call(). This is the very clear recommendation in the NEWS file of the most recent R release:

* .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.

Let's stress the .Call() is much preferred one more time. See eg Hadley's Advanced R Programming draft for more on getting going with .Call(). His advice too is to straight to Rcpp.

2
votes

You already know the errors in the following line.

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

I think, you should write a helper function that does all the work, simplifying both in the process.

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);
}
0
votes

fibonacci is declared to take two pointers as arguments, and it doesn't return anything. But in your else block, you're trying to call it with a single integer argument and capture a return value from it.

Try something like this in your else block:

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

Also, in your else if block, that should be (*n == 0).