Datové struktury v C
Propojení C pomocí funkce .Call() vyžaduje použití speciálních datových typů. Základním typem, ve kterém jsou na úrovni C uložené všechny objekty R je SEXP (S-expresion), proto všechny funkce musí vracet a příjímat argumenty jako SEXP. SEXP je obecný typ. Pro konkrétní datové typy které známe z R existuje mnoho specifických SXP typů. Nejběžnějšími jsou:
- REALSXP: numerický vektor
- INTSXP: vektor celých čísel
- LGLSXP: logický vektor
- STRSXP: znakový vektor
- VECSXP: seznam
- CLOSXP: funkce
Existuji samozřejmě i SXP typy pro méně použivané typy objektů:
- CPLXSXP: komplexní vektory
- LISTSXP: "párový" seznam (z jazyka LISP)
- DOTSXP: '...'
- NILSXP: NULL
Balík pryr obsahuje funkci sexp_type(), která vrací SEXP podtyp pro objekt R.
Načteme balík:
> library(pryr)
Příklady:
> sexp_type(4)
[1] "REALSXP"
> sexp_type("a")
[1] "STRSXP"
> sexp_type(TRUE)
[1] "LGLSXP"
> sexp_type(list(a=1))
[1] "VECSXP"
V souboru Rinternals.h jsou take definované funkce pro převod SEXP vektorů délky jedna na skalár základního typu C (R nezná skalár, pouze vektor o délce jedna):
asLogical(): převádí LGLSXP na int
asInteger(): převádí INTSXP na int
asReal(): převádí REALSXP na double
CHAR(asChar()): převádí STRSXP na const char*
Nebo v opačném směru
ScalarLogical(): převádí int na LGLSXP
ScalarInteger(): převádí int na INTSXP
ScalarReal(): převádí double na REALSXP
mkString(): převádí const char* na STRSXP
Pro přístup k prvkům numerických, celočíselných nebo logických vektorů slouží funkce REAL(), INTEGER(), LOGICAL(). Pro demonstraci jejich použití vytvoříme funkci add_one(), která přičte ke každému prvku celočíselného vektoru jedničku. Všimněte si také, že v C se vektory indexují od nuly, ne od jedničky!
> add_one <- cfunction(c(x = "numeric"), "
+ int n = length(x);
+ SEXP out = PROTECT(allocVector(REALSXP, n));
+
+ for (int i = 0; i < n; i++) {
+ REAL(out)[i] = REAL(x)[i] + 1;
+ }
+ UNPROTECT(1);
+
+ return out;
+ ")
> add_one(as.numeric(1:10))
[1] 2 3 4 5 6 7 8 9 10 11
Vytváření a ochrana vektorů:
Nejjednodušším způsobem vytváření nových vektorů je použití funcke allocVector(). Funkce má 2 argumenty: SEXP podtyp a délku vektoru. Např. následující funkce vytváří a vrací numerický vektor délky 4:
> library(inline)
> dummy <- cfunction(body = '
+ SEXP vektor = PROTECT(allocVector(REALSXP, 4));
+ UNPROTECT(1);
+ return vektor;
+ ')
> dummy()
[1] 2.108492e-265 5.773281e-252 1.003520e-253 1.003494e-253
Všimněte si, že vektor není prázdný, ale obsahuje konkrétní hodnoty a při opakovaném vyvolání funkce dummy() se hodnoty liší. Jde o hodnotu buněk paměti, které byly pro vektor alokované. Protože R obsahuje tzv. garbage collector, který odstraňuje nepoužívané objekty, je nutné vytvořené objekty v C speciálně chránit pomoci funkce PROTECT(). Na konci výpočtu MUSÍME všechny chráněné objekty uvolnit pomoci funkce UNPROTECT(), jinak by došlo k zaplnění paměti pro R.