R из C - самый простой возможный мир - PullRequest
18 голосов
/ 17 марта 2010

Какая самая простая из возможных функций C для запуска интерпретатора R, передачи небольшого выражения (например, 2 + 2) и получения результата?Я пытаюсь скомпилировать с MingW на Windows.

Ответы [ 4 ]

10 голосов
/ 17 марта 2010

Вы хотите позвонить R из C?

Посмотрите раздел 8.1 в руководстве по написанию расширений . Вы также должны заглянуть в каталог «tests» (скачайте пакет с исходным кодом, распакуйте его, и у вас будет каталог tests). Аналогичный вопрос ранее задавался в R-Help, и был приведен пример :

#include <Rinternals.h> 
#include <Rembedded.h> 

SEXP hello() { 
  return mkString("Hello, world!\n"); 
} 

int main(int argc, char **argv) { 
  SEXP x; 
  Rf_initEmbeddedR(argc, argv); 
  x = hello(); 
  return x == NULL;             /* i.e. 0 on success */ 
} 

Простой пример из руководства по R выглядит так:

 #include <Rembedded.h>

 int main(int ac, char **av)
 {
     /* do some setup */
     Rf_initEmbeddedR(argc, argv);
     /* do some more setup */

     /* submit some code to R, which is done interactively via
         run_Rmainloop();

         A possible substitute for a pseudo-console is

         R_ReplDLLinit();
         while(R_ReplDLLdo1() > 0) {
           add user actions here if desired
         }
      */
     Rf_endEmbeddedR(0);
     /* final tidying up after R is shutdown */
     return 0;
 }

Кстати, вы могли бы рассмотреть возможность использования Rinside вместо этого: Дирк предоставляет хороший пример "Hello World" на домашней странице проекта.

Если вы заинтересованы в вызове C из R, вот мой оригинальный ответ:

Это не совсем "привет мир", но вот несколько хороших ресурсов:

9 голосов
/ 17 марта 2010

Вот, пожалуйста. Это основная функция, но вы должны иметь возможность адаптировать ее к более общей функции. В этом примере строится выражение R из вызовов C, а также из строки C. Вы сами по себе для компиляции в Windows, но я предоставил шаги по компиляции в Linux:

 /* simple.c */
 #include <Rinternals.h>
 #include <Rembedded.h>
 #include <R_ext/Parse.h>
 int
 main(int argc, char *argv[])
 {
    char *localArgs[] = {"R", "--no-save","--silent"};
    SEXP e, tmp, ret;
    ParseStatus status;
    int i;

    Rf_initEmbeddedR(3, localArgs);

    /* EXAMPLE #1 */

    /* Create the R expressions "rnorm(10)" with the R API.*/
    PROTECT(e = allocVector(LANGSXP, 2));
    tmp = findFun(install("rnorm"), R_GlobalEnv);
    SETCAR(e, tmp);
    SETCADR(e, ScalarInteger(10));

    /* Call it, and store the result in ret */
    PROTECT(ret = R_tryEval(e, R_GlobalEnv, NULL));

    /* Print out ret */
    printf("EXAMPLE #1 Output: ");
    for (i=0; i<length(ret); i++){
        printf("%f ",REAL(ret)[i]);
    }
    printf("\n");

    UNPROTECT(2);


    /* EXAMPLE 2*/

    /* Parse and eval the R expression "rnorm(10)" from a string */
    PROTECT(tmp = mkString("rnorm(10)"));
    PROTECT(e = R_ParseVector(tmp, -1, &status, R_NilValue));
    PROTECT(ret = R_tryEval(VECTOR_ELT(e,0), R_GlobalEnv, NULL));

    /* And print. */
    printf("EXAMPLE #2 Output: ");
    for (i=0; i<length(ret); i++){
        printf("%f ",REAL(ret)[i]);
    }
    printf("\n");

    UNPROTECT(3);
    Rf_endEmbeddedR(0);
    return(0);
 }

Шаги компиляции:

$ gcc -I/usr/share/R/include/ -c -ggdb simple.c
$ gcc -o simple simple.o  -L/usr/lib/R/lib -lR
$ LD_LIBRARY_PATH=/usr/lib/R/lib R_HOME=/usr/lib/R ./simple
EXAMPLE #1 Output: 0.164351 -0.052308 -1.102335 -0.924609 -0.649887 0.605908 0.130604 0.243198 -2.489826 1.353731
EXAMPLE #2 Output: -1.532387 -1.126142 -0.330926 0.672688 -1.150783 -0.848974 1.617413 -0.086969 -1.334659 -0.313699
7 голосов
/ 17 ноября 2011

Не думаю, что кто-либо из вышеперечисленных ответил на вопрос - что было оценивать 2 + 2;). Использовать строковое выражение было бы что-то вроде:

#include <Rinternals.h>
#include <R_ext/Parse.h>
#include <Rembedded.h>

int main(int argc, char **argv) {
    SEXP x;
    ParseStatus status;
    const char* expr = "2 + 2";

    Rf_initEmbeddedR(argc, argv);

    x = R_ParseVector(mkString(expr), 1, &status, R_NilValue);
    if (TYPEOF(x) == EXPRSXP) { /* parse returns an expr vector, you want the first */
        x = eval(VECTOR_ELT(x, 0), R_GlobalEnv);
        PrintValue(x);
    }

    Rf_endEmbeddedR(0);

    return 0;
}

Здесь явно нет проверки ошибок, но работает:

Z:\>gcc -o e.exe e.c -IC:/PROGRA~1/R/R-213~1.0/include -LC:/PROGRA~1/R/R-213~1.0/bin/i386 -lR
Z:\>R CMD e.exe
[1] 4

(Для получения правильных команд для вашего R используйте R CMD SHLIB e.c, который дает вам соответствующие флаги компилятора)

Вы также можете создать выражение вручную, если оно достаточно простое - например, для rnorm(10) вы бы использовали

SEXP rnorm = install("rnorm");
SEXP x = eval(lang2(rnorm, ScalarInteger(10)), R_GlobalEnv);
4 голосов
/ 17 марта 2010

Я думаю, что вы не можете сделать намного лучше, чем пакет inline (который поддерживает C, C ++ и Fortran):

library(inline)
fun <- cfunction(signature(x="ANY"), 
                 body='printf("Hello, world\\n"); return R_NilValue;')
res <- fun(NULL)

, которая напечатает «Hello, World» для вас. И вы даже не знаете, где / как / когда вызывается компилятор и компоновщик. [R_NilValue - это NULL-версия R SEXP, а используемая здесь подпись .Call() требует, чтобы вы вернули SEXP - см. Руководство «Writing R Extensions», которого вы не можете избежать здесь. ]

Затем вы возьмете такой код и свернете его в пакет. У нас был большой успех с использованием встроенный для Rcpp модульные тесты (более 200 и счет сейчас) и некоторые примеры.

О, и этот встроенный пример будет работать на любой ОС. Даже Windoze при условии, что у вас установлена ​​цепочка инструментов для сборки пакетов R, в PATH и т. П.

Редактировать: Я неправильно понял вопрос. По сути, вам нужно то, что делает интерфейс littler (с использованием чистого C) и что классы RInside разрабатываются для C ++.

Джефф и я никогда не беспокоились о переносе littler на Windoze, но RInside действительно работали там в последнем выпуске. Таким образом, вы сможете поэкспериментировать с рецептами сборки и создать вариант C-only RInside , чтобы можно было передавать выражения во встроенный процесс R. Я подозреваю, что вы все еще хотите что-то вроде Rcpp для подсказки, поскольку в противном случае это становится утомительным.

Редактировать 2: И, как упоминает Шейн, действительно есть несколько примеров в источниках R в тестах / Embedding / вместе с Makefile.win. Может быть, это самое простое начало, если вы хотите узнать о внутренностях R.

...