了解如何在R中处理.Internal C函数

时间:2013-10-29 16:16:33

标签: c r syntax internals

我想知道是否有人可以向我说明R如何从在控制台提示符下键入的R命令执行C调用。我对R处理a)函数参数和b)函数调用本身感到特别困惑。

我们举一个例子,在这种情况下set.seed()。想知道它是如何工作的我在提示符下键入了名称,获取了源代码(look here for more on that),看到最终有.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;
}
  • 什么是CARCADRCADDR?我的研究让我相信它们是一个Lisp受影响的关于列表的构造但除此之外我不明白这些函数的作用或为什么需要它们
  • checkArity()做什么?
  • SEXP args似乎是不言自明的,但这是一个列表 在函数调用中传递的参数?
  • SEXP op代表什么?我认为这意味着运算符(如+等二进制函数),但是SEXP call是什么?

是否有人能够浏览我输入

时发生的事情
set.seed(1)

在R控制台提示符处,直到定义skindnkind的位置?我发现我无法很好地理解这个级别的源代码以及从解释器到C函数的路径。

2 个答案:

答案 0 :(得分:23)

CARCDR是您访问pairlist对象的方式,如section 2.1.11 of R Language Definition中所述。 CAR包含第一个元素,CDR包含其余元素。 section 5.10.2 of Writing R Extensions中给出了一个例子:

#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/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}},

请注意,上表中的arity为4,因为(即使rowSums等只有3个参数)do_colsum有4个,您可以从{{1}看到调用.Internal

rowSums

答案 1 :(得分:18)

基本的C级pairlist提取函数是CARCDR。 (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]]

访问pairlist的第n个元素是一个O(n)操作,与访问O(1)列表的第n个元素不同。这就是为什么没有更好的功能来访问pairlist的第n个元素。

内部/原始函数不按名称进行匹配,它们只使用位置匹配,这就是为什么他们可以使用这个简单的系统来提取参数。

接下来,您需要了解C函数的参数是什么。我不确定这些文件在哪里记录,所以我可能不完全正确的结构,但我应该是一般的部分:

  • callmatch.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源的本地副本是非常有帮助的。