2013-10-29 15 views
38
behandelt werden

Ich frage mich, ob mir jemand veranschaulichen kann, wie R einen C Aufruf von einem R-Befehl ausführt, der an der Eingabeaufforderung der Konsole eingegeben wird. Ich bin besonders verwirrt durch R 's Behandlung von a) Funktion Argumente und b) der Funktionsaufruf selbst.Verstehen, wie .Internal C-Funktionen in R

Nehmen wir ein Beispiel, in diesem Fall set.seed(). Ich frage mich, wie es funktioniert Ich gebe den Namen an der Eingabeaufforderung ein, bekomme die Quelle (look here for more on that), siehe dort ist schließlich ein .Internal(set.seed(seed, i.knd, normal.kind), also pflichtbewusst den relevanten Funktionsnamen im .Internals Abschnitt von /src/names.c nachschlagen, finde es heißt do_setseed und ist in RNG.c, die mich führt zu ...

SEXP attribute_hidden do_setseed (SEXP call, SEXP op, SEXP args, SEXP env) 
{ 
    SEXP skind, nkind; 
    int seed; 

    checkArity(op, args); 
    if(!isNull(CAR(args))) { 
    seed = asInteger(CAR(args)); 
    if (seed == NA_INTEGER) 
     error(_("supplied seed is not a valid integer")); 
    } else seed = TimeToSeed(); 
    skind = CADR(args); 
    nkind = CADDR(args); 
    //... 
     //DO RNG here 
    //... 
    return R_NilValue; 
} 
  • Was sind CAR, CADR, CADDR? Meine Forschung führt mich zu glauben, dass sie ein Lisp beeinflusstes Konstrukt betreffend Listen sind, aber darüber hinaus verstehe ich nicht, was diese Funktionen tun oder , warum sie benötigt werden.
  • Was macht checkArity()?
  • SEXP args scheint selbsterklärend, aber ist dies eine Liste der Argumente, die im Funktionsaufruf übergeben wird?
  • Was bedeutet SEXP op? Ich nehme das Operator (wie in binären Funktionen wie +), aber was ist dann die SEXP call für?

Ist jemand in der Lage durchfließt, was passiert, wenn I

set.seed(1) 

an der Konsole R Aufforderung bis zu dem Punkt, an dem skind und nkind definiert sind,? Ich finde, ich bin nicht in der Lage, den Quellcode auf dieser Ebene und den Weg vom Interpreter zur C-Funktion gut zu verstehen.

+6

Vielleicht möchten Sie beginnen bei http://adv-r.had.co.nz/C-interface.html - ich werde in Kürze einige weitere Details für dieses spezielle Beispiel aufschreiben. – hadley

+0

@hadley danke, ich werde das heute Abend durchmachen. Ich warte wirklich darauf, dass dein Buch veröffentlicht wird! Bald, hoffe ich. :-) –

+0

@ SimonO101: Ich würde hinzufügen "... in R" in Ihrer Frage Titel –

Antwort

23

CAR und CDR sind, wie Sie auf pairlist Objekte zugreifen, wie in section 2.1.11 of R Language Definition erklärt. CAR enthält das erste Element und CDR enthält die restlichen Elemente. Ein Beispiel ist in section 5.10.2 of Writing R Extensions gegeben:

#include <R.h> 
#include <Rinternals.h> 

SEXP convolveE(SEXP args) 
{ 
    int i, j, na, nb, nab; 
    double *xa, *xb, *xab; 
    SEXP a, b, ab; 

    a = PROTECT(coerceVector(CADR(args), REALSXP)); 
    b = PROTECT(coerceVector(CADDR(args), REALSXP)); 
    ... 
} 
/* The macros: */ 
first = CADR(args); 
second = CADDR(args); 
third = CADDDR(args); 
fourth = CAD4R(args); 
/* provide convenient ways to access the first four arguments. 
* More generally we can use the CDR and CAR macros as in: */ 
args = CDR(args); a = CAR(args); 
args = CDR(args); b = CAR(args); 

Es gibt auch ein TAG Makro, um die Namen zu den tatsächlichen Argumente gegeben zuzugreifen.

checkArity stellt sicher, dass die Anzahl der an die Funktion übergebenen Argumente korrekt ist. args sind die tatsächlichen Argumente, die an die Funktion übergeben werden. op ist Offset-Zeiger "verwendet für C-Funktionen, die sich mit mehr als einer R-Funktion befassen" (zitiert aus src/main/names.c, die auch die Tabelle enthält, die den Offset und die Arity für jede Funktion zeigt).

Zum Beispiel do_colsum behandelt col/rowSums und col/rowMeans.

/* Table of .Internal(.) and .Primitive(.) R functions 
* =====  =========  ========== 
* Each entry is a line with 
* 
* printname c-entry  offset eval arity pp-kind precedence rightassoc 
* --------- -------  ------ ---- ----- ------- ---------- ---------- 
{"colSums", do_colsum, 0,  11, 4,  {PP_FUNCALL, PREC_FN, 0}}, 
{"colMeans", do_colsum, 1,  11, 4,  {PP_FUNCALL, PREC_FN, 0}}, 
{"rowSums", do_colsum, 2,  11, 4,  {PP_FUNCALL, PREC_FN, 0}}, 
{"rowMeans", do_colsum, 3,  11, 4,  {PP_FUNCALL, PREC_FN, 0}}, 

Beachten Sie, dass arity in der obigen Tabelle ist 4, da (obwohl rowSums et al haben nur 3 Argumente) do_colsum 4, die Sie aus dem .Internal Anruf in rowSums sehen:

> rowSums 
function (x, na.rm = FALSE, dims = 1L) 
{ 
    if (is.data.frame(x)) 
     x <- as.matrix(x) 
    if (!is.array(x) || length(dn <- dim(x)) < 2L) 
     stop("'x' must be an array of at least two dimensions") 
    if (dims < 1L || dims > length(dn) - 1L) 
     stop("invalid 'dims'") 
    p <- prod(dn[-(1L:dims)]) 
    dn <- dn[1L:dims] 
    z <- if (is.complex(x)) 
     .Internal(rowSums(Re(x), prod(dn), p, na.rm)) + (0+1i) * 
      .Internal(rowSums(Im(x), prod(dn), p, na.rm)) 
    else .Internal(rowSums(x, prod(dn), p, na.rm)) 
    if (length(dn) > 1L) { 
     dim(z) <- dn 
     dimnames(z) <- dimnames(x)[1L:dims] 
    } 
    else names(z) <- dimnames(x)[[1L]] 
    z 
} 
+0

+1 Danke! Das ist toll. 'CheckArity' zählt effektiv die Anzahl der Argumente im Funktionsaufruf, sucht die Funktion und zählt, wie viele Argumente diese Funktion erwartet und wenn sie nicht übereinstimmen, löst sie einen gesteuerten Fehler aus, um die Funktion ordnungsgemäß zu beenden, anstatt die Funktion aufzurufen mit der falschen Anzahl von Argumenten und möglicherweise Seg-Verwerfung? –

+2

@ SimonO101: Ja, es sieht die Funktion aus der Tabelle in 'names.c' nach, um zu sehen, wie viele Argumente benötigt oder erlaubt sind. Sie können den Fehler sehen, indem Sie Folgendes aufrufen: 'x <- 1; .Internal (rowSums (x)) '. –

18

Die grundlegenden C-Level-Extraktionsfunktionen für den Parallelisten sind CAR und CDR. (Parallelisten sind Listen sehr ähnlich, sind aber als eine verkettete Liste implementiert und werden intern für Argumentlisten verwendet). Sie haben einfache R-Äquivalente: x[[1]] und x[-1]. R stellt auch viele Kombinationen der beiden:

  • CAAR(x) = CAR(CAR(x)) die zu x[[1]][[1]] entspricht
  • CADR(x) = CAR(CDR(x)) die zu x[-1][[1]] äquivalent ist, dh x[[2]]
  • CADDR(x) = CAR(CDR(CDR(x)) entspricht x[-1][-1][[1]], dh x[[3]]
  • und so auf

Zugriff auf das n-te Element von a Pairlist ist eine O(n) Operation, im Gegensatz zum Zugriff auf das n-te Element einer Liste, die O(1) ist. Aus diesem Grund gibt es keine besseren Funktionen für den Zugriff auf das n-te Element eines Pairlisten.

Interne/primitive Funktionen stimmen nicht mit dem Namen überein, sie verwenden nur Positionsabgleich, weshalb sie dieses einfache System zum Extrahieren der Argumente verwenden können.

Als nächstes müssen Sie verstehen, was die Argumente für die C-Funktion sind. Ich bin mir nicht sicher, wo diese dokumentiert werden, so dass ich vielleicht nicht ganz richtig über die Struktur, aber ich soll die allgemeinen Stücke sein:

  • call: der komplette Anruf, wie sie durch match.call()

  • erfaßt werden
  • op: Der Index der .Internal-Funktion, der von R aufgerufen wird. Dies wird benötigt, da es eine Viele-zu-1-Zuordnung von .Interner Funktionen zu C-Funktionen gibt. (z. B. do_summary implementiert Summe, Mittelwert, Min, Max und Prod). Die Zahl ist der dritte Eintrag in names.c - es ist immer 0 für do_setseed und daher nie

    verwendet
  • args: ein Paar Liste der für die Funktion angegebenen Argumente.

  • env: die Umgebung, von der die Funktion aufgerufen wurde.

checkArity ist ein Makro, das Rf_checkArityCall nennt, die im Grunde die Anzahl der Argumente sucht (die fünfte Spalte in names.c ist arity) und stellen Sie sicher, dass die gelieferten Nummer übereinstimmt. Sie müssen einige Makros und Funktionen in C durchlaufen, um zu sehen, was vor sich geht - es ist sehr hilfreich, eine lokale Kopie der R-Quelle zu haben, die Sie durchforsten können.

+0

+1 Danke! Die Verwendung von say 'CADDR (x)' hält effektiv die Zugriffszeit auf 'O (1) '(Ich hätte gedacht, dass die Zeitstrafe für eine Operation wie diese minimal wäre, da ich nicht an a denken kann Funktion, bei der die Argumentliste * lang * lang würde? Es macht jetzt * viel * mehr Sinn für mich, das obige Beispiel zu lesen (kombiniert mit Ihrem Buchkapitel, das ich gerade lese). –

+0

@ SimonO101 Nein, 'CADDR (x) 'wird langsamer sein als' CDR (x) ', aber Sie können' CAR' und 'CDR' zusammen verwenden, wie in Joshua's Beispiel, um den Overhead zu vermeiden. – hadley