[英]Understanding how .Internal C functions are handled in R
我想知道是否有人可以向我说明R如何从控制台提示符下键入的R命令执行C
调用。 我对R
对a)函数参数和b)函数调用本身的处理特别感到困惑。
我们举一个例子,在这种情况下是set.seed()
。 想知道它是如何工作的我在提示符下输入名称,获取源代码( 在这里查看更多内容 ),看看最终有一个.Internal(set.seed(seed, i.knd, normal.kind)
,所以尽职尽责在/src/names.c
的.Internals
部分查找相关的函数名,找到它叫做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函数的路径。
CAR
和CDR
是您访问pairlist对象的方式,如R语言定义的 2.1.11节所述 。 CAR
包含第一个元素, CDR
包含其余元素。 编写R扩展的第5.10.2节给出了一个例子:
#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
是偏移指针“用于处理多个R函数的C函数”(引自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
等只有3个参数) do_colsum
有4个,您可以从rowSums
的.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
}
基本的C级pairlist提取功能是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(1)
不同,访问pairlist的第n个元素是O(n)
操作。 这就是为什么没有更好的功能来访问pairlist的第n个元素。
内部/原始函数不按名称进行匹配,它们仅使用位置匹配,这就是为什么它们可以使用这个简单的系统来提取参数。
接下来,您需要了解C函数的参数是什么。 我不确定这些文件在哪里记录,所以我可能不完全正确的结构,但我应该是一般的部分:
call
:完成调用,可能由match.call()
捕获
op
:从R调用的.Internal函数的索引。这是必需的,因为从.Internal函数到C函数有多对一的映射。 (例如, do_summary
实现sum,mean,min,max和prod)。 该数字是names.c
的第三个条目 - 对于do_setseed
,它始终为0,因此从未使用过
args
:提供给函数的参数的一对列表。
env
:调用函数的环境。
checkArity
是一个调用Rf_checkArityCall
的宏,它基本上查找参数的数量( names.c
的第五列是arity)并确保提供的数字匹配。 你必须在C中完成相当多的宏和函数才能看到正在发生的事情 - 拥有一个你可以通过的R源的本地副本是非常有帮助的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.