Verstehen, wie .interne C-Funktionen in R gehandhabt werden

Lesezeit: 7 Minuten

Benutzeravatar von Simon O'Hanlon
Simon O’Hanlon

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.cfinde 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.

  • 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 erscheint c Tag, ich denke, es ist gerechtfertigt.

    – Josh O’Brien

    29. Oktober 2013 um 23:12 Uhr

Benutzeravatar von Joshua Ulrich
Josua Ulrich

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.cdie 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
}

  • +1 Danke! Das ist toll. So checkArity zählt effektiv die Anzahl der Argumente im Funktionsaufruf, schlägt die Funktion nach und zählt, wie viele Argumente diese Funktion erwartet, und wenn sie nicht übereinstimmen, wird ein kontrollierter Fehler ausgegeben, um die Funktion ordnungsgemäß zu beenden, anstatt die Funktion mit der falschen Nummer aufzurufen von Argumenten und möglicherweise Seg-Faulting?

    – Simon O’Hanlon

    29. Oktober 2013 um 17:14 Uhr


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

    – Josua Ulrich

    29. Oktober 2013 um 17:19 Uhr

Hadleys Benutzer-Avatar
Hadley

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_checkArityCalldas 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.

  • +1 Danke! So auch die Verwendung von say CADDR(x) effektiv die Zugriffszeit zu halten O(1) (Ich hätte gedacht, dass die Zeitstrafe für eine Operation wie diese minimal wäre, da mir keine Funktion einfällt, in die die Argumentliste gelangen würde das lang? Es macht viel Jetzt ist es für mich sinnvoller, mein obiges Beispiel noch einmal zu lesen (kombiniert mit Ihrem Buchkapitel, das ich gerade lese).

    – Simon O’Hanlon

    29. Oktober 2013 um 17:12 Uhr

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

    – hadley

    29. Oktober 2013 um 20:41 Uhr

1394850cookie-checkVerstehen, wie .interne C-Funktionen in R gehandhabt werden

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy