了解如何。内部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; } 
  • 什么是CARCADRCADDR ? 我的研究让我相信他们是一个Lisp影响的关于列表的构造,但除此之外,我不明白这些function是做什么的或者为什么需要它们
  • checkArity()做什么的?
  • SEXP args似乎是自我解释,但是这是在函数调用中传递的参数列表?
  • SEXP op代表什么? 我认为这是意味着运营商(就像+ )这样的二元函数),但是SEXP call什么?

任何人都能够通过我input时发生的事情

 set.seed(1) 

在R控制台提示符下,直到skindnkind被定义的位置? 我发现我不能很好地理解这个级别的源代码和从解释器到C函数的path。

CARCDR是如何访问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); 

还有一个TAGmacros来访问实际参数的名称。

checkArity确保传递给函数的参数个数是正确的。 args是传递给函数的实际参数。 op是用于处理多个R函数的C函数的偏移指针(引用来自src/main/names.c ,其中还包含显示每个函数的偏移和arity的表)。

例如, do_colsum处理col/rowSumscol/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是CARCDR 。 (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源的本地副本是非常有帮助的。