了解如何。内部C函数在R中处理
我想知道是否有人可以向我演示R如何通过在控制台提示符下键入的R命令执行C
调用。 我对R
的处理a)函数参数和b)函数调用本身特别困惑。
我们举一个例子,在这种情况下set.seed()
。 想知道它是如何工作的我在提示中键入名称,获取源代码( 查看更多内容 ),看看最终有一个.Internal(set.seed(seed, i.knd, normal.kind)
在/src/names.c
的.Internals
部分查找相关的函数名,find它叫做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
影响的关于列表的构造,但除此之外,我不明白这些function是做什么的或者为什么需要它们 。 -
checkArity()
做什么的? -
SEXP args
似乎是自我解释,但是这是在函数调用中传递的参数列表? -
SEXP op
代表什么? 我认为这是意味着运营商(就像+
)这样的二元函数),但是SEXP call
什么?
任何人都能够通过我input时发生的事情
set.seed(1)
在R控制台提示符下,直到skind
和nkind
被定义的位置? 我发现我不能很好地理解这个级别的源代码和从解释器到C函数的path。
CAR
和CDR
是如何访问pairlist对象的,如R语言定义的 2.1.11节所述 。 CAR
包含第一个元素, CDR
包含其余元素。 编写R扩展的第5.10.2节给出了一个例子:
#include <Rh> #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
macros来访问实际参数的名称。
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}},
请注意,上表中的rowSums
是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提取function是CAR
和CDR
。 (Pairlists与列表非常相似,但实现为链接列表并在内部用于参数列表)。 它们有简单的R等价物: x[[1]]
和x[-1]
。 R还提供了很多的两个组合:
-
CAAR(x) = CAR(CAR(x))
,相当于x[[1]][[1]]
- 相当于
x[-1][[1]]
CADR(x) = CAR(CDR(x))
,即x[[2]]
-
CADDR(x) = CAR(CDR(CDR(x))
等于x[-1][-1][[1]]
,即x[[3]]
- 等等
访问pairlist的第n个元素是O(n)
操作,而不像访问列表中的第n个元素是O(1)
。 这就是为什么没有更好的function来访问一个pairlist的第十个元素。
内部/原始函数不会按名称进行匹配,它们只使用位置匹配,这就是为什么他们可以使用这个简单的系统来提取参数。
接下来,您需要了解C函数的参数是什么。 我不确定这些logging在哪里,所以我可能不完全正确的结构,但我应该是一般的作品:
-
call
:可能由match.call()
捕获的完整调用 -
op
:从R调用的.Internal函数的索引。这是必需的,因为从.Internal函数到C函数有一个多对1映射。 (例如do_summary
实现总和,平均值,最小值,最大值和产品)。 这个数字是names.c
的第三个条目 –names.c
总是为0,因此从来没有使用过 -
args
:提供给函数的参数对列表。 -
env
:函数被调用的环境。
checkArity
是一个调用Rf_checkArityCall
的macros,它基本上查找了参数的数量( names.c
的第五列是arity),并确保提供的数字匹配。 你必须按照C中的一些macros和函数来看看发生了什么事 – 有一个你可以通过的R源的本地副本是非常有帮助的。