Ich frage mich, ob mir jemand veranschaulichen kann, wie R a ausführt C
Aufruf von einem R-Befehl, der an der Konsoleneingabeaufforderung eingegeben wird. Mich verwirrt besonders R
‘s Behandlung von a) Funktionsargumenten und b) dem Funktionsaufruf selbst.
Nehmen wir in diesem Fall ein Beispiel set.seed()
. Ich frage mich, wie es funktioniert, ich tippe den Namen an der Eingabeaufforderung ein, bekomme die Quelle (Mehr dazu finden Sie hier), siehe da ist schließlich a .Internal(set.seed(seed, i.knd, normal.kind)
also suchen Sie pflichtbewusst den entsprechenden Funktionsnamen in der .Internals
Abschnitt von /src/names.c
finde es heißt do_setseed
und ist dabei RNG.c
was mich zu … führt
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 Nachforschungen lassen mich glauben, dass sie a Lisp
beeinflusstes Konstrukt bzgl. Listen aber darüber hinaus verstehe ich nicht was diese Funktionen machen bzw warum sie gebraucht werden.
- Was macht
checkArity()
tun?
SEXP args
scheint selbsterklärend, aber ist dies eine Liste der Argumente, die im Funktionsaufruf übergeben werden?
- Was macht
SEXP op
vertreten? Ich nehme dies als Operator (wie in binären Funktionen wie z +
), aber was ist dann die SEXP call
zum?
Kann jemand nachvollziehen, was passiert, wenn ich tippe?
set.seed(1)
an der Eingabeaufforderung der R-Konsole bis zu dem Punkt, an dem skind
und nkind
sind festgelegt? Ich finde, ich bin nicht in der Lage, den Quellcode auf dieser Ebene und den Pfad vom Interpreter zur C-Funktion gut zu verstehen.
CAR
und CDR
So greifen Sie auf Pairlist-Objekte zu, wie in erläutert Abschnitt 2.1.11 des R-Sprachdefinition. CAR
enthält das erste Element und CDR
enthält die restlichen Elemente. Ein Beispiel ist in angegeben Abschnitt 5.10.2 des Schreiben von R-Erweiterungen:
#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 auf die Namen zuzugreifen, die den eigentlichen Argumenten gegeben wurden.
checkArity
stellt sicher, dass die Anzahl der an die Funktion übergebenen Argumente korrekt ist. args
sind die eigentlichen Argumente, die an die Funktion übergeben werden. op
ist ein Offset-Zeiger, der “für C-Funktionen verwendet wird, die sich mit mehr als einer R-Funktion befassen” (zitiert aus src/main/names.c
die auch die Tabelle mit Offset und Stellgrad für jede Funktion enthält).
Zum Beispiel, do_colsum
Griffe 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, weil (obwohl rowSums
et al haben nur 3 Argumente) do_colsum
hat 4, die Sie aus dem sehen können .Internal
anrufen rowSums
:
> 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
}
Die grundlegenden Funktionen zum Extrahieren von Paarlisten auf C-Ebene sind CAR
und CDR
. (Paarlisten sind Listen sehr ähnlich, sind aber als verkettete Liste implementiert und werden intern für Argumentlisten verwendet). Sie haben einfache R-Äquivalente: x[[1]]
und x[-1]
. R bietet auch viele Kombinationen der beiden:
CAAR(x) = CAR(CAR(x))
was äquivalent ist x[[1]][[1]]
CADR(x) = CAR(CDR(x))
was äquivalent ist x[-1][[1]]
dh x[[2]]
CADDR(x) = CAR(CDR(CDR(x))
ist äquivalent zu x[-1][-1][[1]]
dh x[[3]]
- usw
Der Zugriff auf das n-te Element einer Paarliste ist ein O(n)
Operation, im Gegensatz zum Zugriff auf das n-te Element einer Liste, das ist O(1)
. Aus diesem Grund gibt es keine schöneren Funktionen für den Zugriff auf das n-te Element einer Paarliste.
Interne/primitive Funktionen führen keinen Abgleich nach Namen durch, sie verwenden nur einen Positionsabgleich, weshalb sie dieses einfache System zum Extrahieren der Argumente verwenden können.
Als nächstes müssen Sie verstehen, was die Argumente der C-Funktion sind. Ich bin mir nicht sicher, wo diese dokumentiert sind, also habe ich vielleicht nicht ganz recht mit der Struktur, aber ich sollte die allgemeinen Stücke sein:
-
call
: der vollständige Anruf, wie er erfasst werden könnte match.call()
-
op
: Der Index der .Internal-Funktion, die von R aufgerufen wird. Dies ist erforderlich, da es eine Viele-zu-1-Zuordnung von .Internal-Funktionen zu C-Funktionen gibt. (z.B do_summary
implementiert sum, mean, min, max und prod). Die Nummer ist der dritte Eintrag in names.c
– es ist immer 0 für do_setseed
und daher nie benutzt
-
args
: eine Paarliste der an die Funktion gelieferten Argumente.
-
env
: die Umgebung, aus der die Funktion aufgerufen wurde.
checkArity
ist ein Makro, das aufruft Rf_checkArityCall
das im Wesentlichen die Anzahl der Argumente nachschlägt (die fünfte Spalte in names.c
ist arity) und vergewissern Sie sich, dass die angegebene Nummer übereinstimmt. Sie müssen einige Makros und Funktionen in C durchgehen, um zu sehen, was vor sich geht – es ist sehr hilfreich, eine lokale Kopie der R-Quelle zu haben, die Sie durchsuchen können.
Vielleicht möchten Sie bei beginnen adv-r.had.co.nz/C-interface.html – Ich werde in Kürze einige weitere Details für dieses spezielle Beispiel aufschreiben.
– hadley
29. Oktober 2013 um 16:41 Uhr
@hadley danke, ich werde das heute Abend durchgehen. Ich warte wirklich auf die Veröffentlichung Ihres Buches! Bald, hoffe ich. 🙂
– Simon O’Hanlon
29. Oktober 2013 um 16:43 Uhr
@ SimonO101: Ich würde “… in R” in Ihren Fragentitel einfügen
– Olivier Dulac
29. Oktober 2013 um 18:49 Uhr
@OlivierDulac – Fertig, danke. Für Fragen, die nur mit getaggt sind
r
Tag, wir runzeln normalerweise die Stirn, aber da dies auch unter dem erscheintc
Tag, ich denke, es ist gerechtfertigt.– Josh O’Brien
29. Oktober 2013 um 23:12 Uhr