Slovník | Vyhledávání | Mapa webu
 
Základy informatiky pro biologyAnalýza dat v R Propojení C a R Odkazování na C funkci v R

Logo Matematická biologie

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é zdehttp://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:

Jiným způsobem propojení jazyků C a R je funkce .Call() Jeji hlavní odlišnosti od funkce .C() je práce s objekty R definovanými jako speciální datové typy v souborech R.h a Rinternals.R, které je nutno zahrnout do implementace pomocí direktivy #include. Dále je tento nástroj pro propojení C a R novější, je podporován knihovnou inline, umožňuje přístup k atributům objektu a vnoření R kódu.

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

 

 
vytvořil Institut biostatistiky a analýz Lékařské fakulty Masarykovy univerzity