Подтвердить что ты не робот

Понимание того, как. Внутренние функции C обрабатываются в R

Интересно, может ли кто-нибудь показать мне, как R выполняет вызов C из команды R, введенной в командной строке. Меня особенно смущает R обработка аргументов функции) и б) сам вызов функции.

Пусть возьмем пример, в данном случае set.seed(). Удивительно, как это работает. Я ввожу имя в приглашении, получаю источник ( здесь, чтобы узнать больше об этом), см., В конце концов, a .Internal(set.seed(seed, i.knd, normal.kind), так послушно найдите соответствующее имя функции в разделе .Internals /src/names.c, найдите, что он называется do_setseed и находится в RNG.c, что приводит меня к...

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;
}
  • Что такое CAR, CADR, CADDR? Мои исследования заставляют меня думать, что они являются конструкцией, влияющей на Lisp, касающейся списков, но помимо этого я не понимаю, что делают эти функции или почему они необходимы.
  • Что делает checkArity()?
  • SEXP args кажется самоочевидным, но это список аргументы, которые передаются в вызове функции?
  • Что представляет SEXP op? Я считаю, что это означает оператор (например, в двоичных функциях, таких как +), но тогда что такое SEXP call для?

Кто-нибудь может протекать через то, что происходит, когда я печатаю

set.seed(1)

в командной строке R, до точки, в которой определены skind и nkind? Я нахожу, что не могу понять исходный код на этом уровне и путь от интерпретатора до функции C.

4b9b3361

Ответ 1

CAR и CDR - это то, как вы получаете доступ к объектам парного списка, как описано в разделе раздела 2.1.11 определения языка R. CAR содержит первый элемент, а CDR содержит остальные элементы. Пример приведен в разделе раздела 5.10.2 расширений Writing 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);

Также существует макрос TAG для доступа к именам, заданным для фактических аргументов.

checkArity гарантирует правильность количества аргументов, переданных функции. args - фактические аргументы, переданные функции. op является указателем смещения ", используемым для функций C, которые имеют дело с более чем одной функцией R" (цитируется из src/main/names.c, которая также содержит таблицу, показывающую смещение и arity для каждой функции).

Например, do_colsum обрабатывает col/rowSums и 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}},

Обратите внимание, что arity в приведенной выше таблице 4, потому что (хотя rowSums et al только имеет 3 аргумента) do_colsum имеет 4, которые вы можете увидеть по вызову .Internal в 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
}

Ответ 2

Основные функции извлечения списка пар файлов CAR и CDR. (Pairlists очень похожи на списки, но реализованы как связанные списки и используются внутри для списков аргументов). Они имеют простые R-эквиваленты: x[[1]] и x[-1]. R также обеспечивает множество комбинаций из двух:

  • CAAR(x) = CAR(CAR(x)), что эквивалентно x[[1]][[1]]
  • CADR(x) = CAR(CDR(x)), что эквивалентно x[-1][[1]], т.е. x[[2]]
  • CADDR(x) = CAR(CDR(CDR(x)) эквивалентен x[-1][-1][[1]], т.е. x[[3]]
  • и т.д.

Доступ к n-му элементу парного списка - это операция O(n), в отличие от доступа к n-му элементу списка O(1). Вот почему нет более удобных функций для доступа к n-му элементу парного списка.

Внутренние/примитивные функции не выполняют сопоставление по имени, они используют только позиционное сопоставление, поэтому они могут использовать эту простую систему для извлечения аргументов.

Далее вам нужно понять, что аргументы функции C. Я не уверен, где они задокументированы, поэтому я могу быть не совсем прав в отношении структуры, но я должен быть общим:

  • call: полный вызов, который может быть захвачен match.call()

  • op: индекс внутренней функции, вызванной из R. Это необходимо, потому что есть много-к-1-отображение из. Внутренние функции в C-функции. (например, do_summary реализует сумму, среднее значение, min, max и prod). Число - это третья запись в names.c - она ​​всегда 0 для do_setseed и, следовательно, никогда не использовалась

  • args: список пар аргументов, переданных функции.

  • env: среда, из которой вызывается функция.

checkArity - это макрос, который вызывает Rf_checkArityCall, который в основном ищет количество аргументов (пятый столбец в names.c - это arity) и убедитесь, что указанный номер соответствует. Вам нужно пройти через несколько макросов и функций на C, чтобы увидеть, что происходит - очень полезно иметь локальную копию R-источника, с которой вы можете пройти.