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

Sim*_*lon 39 c syntax r internals

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

在这种情况下,我们举一个例子set.seed().想知道它是如何工作我在提示符下键入名称,获得源(看看这里的更多介绍),看到有最后一个.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;
}
Run Code Online (Sandbox Code Playgroud)
  • 什么是CAR,CADR,CADDR?我的研究让我相信它们是一个Lisp有关列表的受影响的结构,但除此之外,我不明白这些功能的作用或为什么需要它们.
  • 怎么checkArity()办?
  • SEXP args 似乎是自解释的,但这是函数调用中传递的参数列表吗?
  • 什么是SEXP op代表?我认为这意味着运算符(比如二进制函数+),但那么它是什么SEXP call

是否有人能够流经我打字时发生的事情

set.seed(1)
Run Code Online (Sandbox Code Playgroud)

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

Jos*_*ich 23

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);
Run Code Online (Sandbox Code Playgroud)

还有一个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}},
Run Code Online (Sandbox Code Playgroud)

请注意,arity上表中的4是因为(尽管rowSumset 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
}
Run Code Online (Sandbox Code Playgroud)

  • @ SimonO101:是的,它从`names.c`中的表中查找函数,看看需要或允许多少个args.您可以通过调用类似以下内容来查看错误:`x < - 1; .内部(rowSums(X))`. (2认同)

had*_*ley 19

基本的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)操作,不像访问列表的第n个元素O(1).这就是为什么没有更好的功能来访问pairlist的第n个元素.

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

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

  • call:完整的呼叫,可能被捕获 match.call()

  • op:从R调用的.Internal函数的索引.这是必需的,因为从.Internal函数到C函数有多对一的映射.(例如do_summary实现sum,mean,min,max和prod).这个数字是第三个条目names.c- 它始终为0 do_setseed,因此从未使用过

  • args:提供给函数的参数的一对列表.

  • env:调用函数的环境.

checkArity是一个调用的宏Rf_checkArityCall,它基本上查找参数的数量(names.carity中的第五列),并确保提供的数字匹配.你必须在C中完成相当多的宏和函数才能看到正在发生的事情 - 拥有一个你可以通过的R源的本地副本是非常有帮助的.