
Odkazování na C funkci v R
Pro urychlení výpočtu je vhodné přeprogramovat důležité části kódu do C. V téhle kapitole si ukážeme jak propojit C a R dvěma způsoby. Propojení budeme demonstrovat na příkladu jednoduché funkce, která sčítá dvě čísla.
Způsob propojení 1:
Jako první způsob použijeme funkci .C() ze základního balíku base. Použití funkce .C() představuje starší způsob propojení C a R. Funkce kopíruje R objekty před volaním C kódu, stejně tak kopíruje výstupy z kódu do R objektů. Ve funkci se změní pouze hodnota argumentů.
Nejdřív v textovém editoru vytvoříme soubor prikladC.c, který uložíme v pracovním adresáři. Soubor obsahuje nasledujíci kód:
/* prikladC.c */
void prikladC(int *a, int *b, double *result) {
result[0] = *a + *b;
}
Za komentářem /* prikladC.c */ následuje defince funkce prikladC s návratovou hodnotou typu void. Funkce má tři argumenty: 2 celé čísla a a b a výsledek typu double (má větší rozsah jako int)
Všimněte si, že implementace funkcí v C pro R je specifická v několika ohledech:
- návratová hodnota funkce je typu void, t.j. funkce nevrací žádnou hodnotu
- všechny vstupy a výstupy jsou předávány jako argumenty funkce
- implementace pracuje se základními datovými typy jazyka C jako int, double
- funkci se nepředávají hodnoty argumentů, ale ukazatele na místa v paměti (*a místo a), funkce tak mění přímo objekty z R
Zdrojový kód funkce je nutno přeložit a zkompilovat, proto v příkazovém řádku v pracovním adresáři zadáme:
R CMD SHLIB prikladC.c
Pro systém Windows je nuté mít nainstalované R tools dostupné zde: http://cran.r-project.org/bin/windows/Rtools/
Kompilátor vytvoří dva soubory: prikladC.dll, a prikladC.o. První z nich načteme do R pomocí funkce dyn.load():
> dyn.load(paste(getwd(),"prikladC.dll", sep="/"))
Pozor, je nutno pracovat ve stejné architektuře jaká byla použita pro kompilaci (32 bit nebo 64 bit).
Ověříme načtení:
> is.loaded("prikladC")
[1] TRUE
Vytvoříme objekty, které chceme sčítat:
> a <- 4
> b <- 5
Použijeme funkci .C() i s předáním argumentu. Pro použití implementace C v R je velice doporučováno kontrolovat správnost vstupů nebo používat funkce jako as.integer() na konverzi vstupů na potřebný datový typ.
> out <- .C("prikladC", a1 = as.integer(a), b1=as.integer(b), result=numeric(1 ))
> out
$a1
[1] 4
$b1
[1] 5
$result
[1] 9
Výsledkem je seznam, ve kterém element result obsahuje výsledek výpočtu:
> out$result
[1] 9
Protože funkce pracuje přímo s místy v paměti, dostaneme při nevhodných typech vstupních argumenů pouze nesprávný výsledek.
> .C("prikladC", a1 = c("A","B"), b1=as.integer(b), result=numeric(1 ))
$a1
[1] "A" "B"
$b1
[1] 5
$result
[1] 221379405
Způsob propojení 2:
Podobně jako v předchozím příkladu začneme vytvořením souboru prikladCall.c, který bude obsahovat implementaci funkce:
/* prikladCall.c */
#include <R.h>
#include <Rinternals.h>
SEXP prikladCall(SEXP a, SEXP b) {
SEXP result = PROTECT(allocVector(REALSXP, 1)); REAL(result)[0] = asReal(a) + asReal(b);
UNPROTECT(1);
return result;
}
Detailní popis jednotlivých datových typů a výklad použitých funkcí naleznete v sekci Datové struktury v C.
Podobně jako v předchozím příkladu, funkci zkompilujeme v příkazovém řádku:
R CMD SHLIB prikladCall.c
Do R ji pak načteme pomocí dyn.load():
> dyn.load(paste(getwd(),"prikladCall.dll",sep="/"))
Oveříme načtení:
> is.loaded("prikladCall")
[1] TRUE
Funkci add() můžeme použit podobně jako předtím funkci prikladC:
> .Call("prikladCall", a, b)
[1] 9
Při nevhodných argumentech funkce vrací hondotu NA:
> .Call("add", "A", 4)
[1] NA
Warning message:
NAs introduced by coercion
Můžeme také v R vytvořit funkci add(), která využívá implementaci v C:
> add <- function(a, b) { .Call("prikladCall", a, b) }
a používat ji běžným způsobem:
> add(4,5)
[1] 9
Existují ještě další způsoby odvolávání se na funkci C v R:
- .External
- Je téměř identický s .Call()
- Argumenty funkce jsou předávány jako seznam, což je výhodné pro funkce s proměnlivým počtem argumentů
- Tento způsob není rozšířen
- cfunction z balíku inline dovoluje vyvářet funkce bez použití externího zdojového souboru přímým zadaním argumentů i kódu funkce:
> add <- cfunction(c(a = "integer", b = "integer"), "
+ SEXP result = PROTECT(allocVector(REALSXP, 1));
+ REAL(result)[0] = asReal(a) + asReal(b);
+ UNPROTECT(1);
return result; ")
> add(1, 5)
[1] 6