Понимание того, как. Внутренние функции 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.
Ответы
Ответ 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-источника, с которой вы можете пройти.