39 votes

Comprendre comment les fonctions C internes sont gérées dans R

Je me demande si quelqu'un peut m'expliquer comment R exécute un C à partir d'une commande R tapée à l'invite de la console. Je suis particulièrement troublé par R a) des arguments de la fonction et b) de l'appel de la fonction elle-même.

Prenons un exemple, en l'occurrence set.seed() . Je tape le nom à l'invite, j'obtiens la source ( Pour en savoir plus, cliquez ici ), voir qu'il y a finalement un .Internal(set.seed(seed, i.knd, normal.kind) et donc de rechercher consciencieusement le nom de la fonction correspondante dans la base de données .Internals section de /src/names.c , trouver s'appelle do_setseed et est en RNG.c ce qui m'amène à...

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;
}
  • Quels sont les CAR , CADR , CADDR ? Mes recherches m'amènent à penser qu'il s'agit d'une Lisp La construction influencée concernant les listes, mais au-delà de cela, je ne comprends pas ce que ces fonctions font ou ce qu'elles font. pourquoi ils sont nécessaires .
  • Qu'est-ce que checkArity() faire ?
  • SEXP args semble s'expliquer d'elle-même, mais s'agit-il d'une liste des arguments transmis lors de l'appel de la fonction ?
  • Qu'est-ce que SEXP op représenter ? J'entends par là un opérateur (comme dans les fonctions binaires telles que + ), mais alors qu'est-ce que le SEXP call pour ?

Quelqu'un peut-il m'expliquer ce qui se passe lorsque je tape

set.seed(1)

à l'invite de la console R, jusqu'au moment où skind y nkind sont définis ? Je ne suis pas en mesure de bien comprendre le code source à ce niveau et le chemin de l'interpréteur à la fonction C.

23voto

Joshua Ulrich Points 68776

CAR y CDR permettent d'accéder aux objets de la liste de paires, comme l'explique la section le point 2.1.11 du Définition du langage R . CAR contient le premier élément, et CDR contient les éléments restants. Un exemple est donné dans la section 5.10.2 du Écrire des extensions R :

#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);

Il y a également un TAG pour accéder aux noms donnés aux arguments réels.

checkArity s'assure que le nombre d'arguments transmis à la fonction est correct. args sont les arguments réels transmis à la fonction. op est un pointeur de décalage "utilisé pour les fonctions C qui traitent avec plus d'une fonction R" (cité dans src/main/names.c qui contient également le tableau indiquant le décalage et l'arité de chaque fonction).

Par exemple, do_colsum poignées col/rowSums y 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}},

Il convient de noter que arity dans le tableau ci-dessus est 4 parce que (même si rowSums et al n'ont que 3 arguments) do_colsum en a 4, ce que vous pouvez voir dans le .Internal appeler 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
}

19voto

hadley Points 33766

Les fonctions de base d'extraction de listes de paires au niveau C sont les suivantes CAR y CDR . (Les listes de paires sont très similaires aux listes mais sont implémentées comme des listes liées et sont utilisées en interne pour les listes d'arguments). Elles ont des équivalents simples en R : x[[1]] y x[-1] . R offre également de nombreuses combinaisons des deux :

  • CAAR(x) = CAR(CAR(x)) ce qui équivaut à x[[1]][[1]]
  • CADR(x) = CAR(CDR(x)) ce qui équivaut à x[-1][[1]] , c'est-à-dire x[[2]]
  • CADDR(x) = CAR(CDR(CDR(x)) est équivalent à x[-1][-1][[1]] , c'est-à-dire x[[3]]
  • et ainsi de suite

L'accès au nième élément d'une liste de paires est une opération de O(n) contrairement à l'accès au nième élément d'une liste qui est O(1) . C'est pourquoi il n'existe pas de fonctions plus agréables pour accéder au nième élément d'une liste de paires.

Les fonctions internes/primitives ne font pas de correspondance par nom, elles n'utilisent que la correspondance positionnelle, ce qui explique pourquoi elles peuvent utiliser ce système simple pour extraire les arguments.

Ensuite, vous devez comprendre quels sont les arguments de la fonction C. Je ne sais pas où ces arguments sont documentés, et il se peut donc que je n'aie pas tout à fait raison en ce qui concerne la structure, mais je devrais en donner les grandes lignes :

  • call l'appel complet, tel qu'il pourrait être capturé par match.call()

  • op : l'index de la fonction .Internal appelée à partir de R. Ceci est nécessaire parce qu'il existe une correspondance de plusieurs à un entre les fonctions .Internal et les fonctions C. (par ex. do_summary met en œuvre la somme, la moyenne, le min, le max et le prod). Le nombre est la troisième entrée de names.c - c'est toujours 0 pour do_setseed et n'a donc jamais utilisé

  • args : une liste de paires d'arguments fournis à la fonction.

  • env : l'environnement à partir duquel la fonction a été appelée.

checkArity est une macro qui appelle Rf_checkArityCall qui calcule le nombre d'arguments (la cinquième colonne du tableau de bord). names.c est l'arité) et s'assurer que le nombre fourni correspond. Il faut suivre un certain nombre de macros et de fonctions en C pour comprendre ce qui se passe - il est très utile d'avoir une copie locale des sources de R que l'on peut consulter.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X