如果我们在R base source代码中查看用C编写的任何函数,我们可以看到代码非常简单,例如。
#include "nmath.h"
#include "dpq.h"
double dexp(double x, double scale, int give_log)
{
#ifdef IEEE_754
/* NaNs propagated correctly */
if (ISNAN(x) || ISNAN(scale)) return x + scale;
#endif
if (scale <= 0.0) ML_ERR_return_NAN;
if (x < 0.)
return R_D__0;
return (give_log ?
(-x / scale) - log(scale) :
exp(-x / scale) / scale);
}
如果从R:
调用该函数,则该函数被矢量化dexp(rep(1, 5), 1:2)
## [1] 0.3678794 0.2706706 0.3678794 0.2706706 0.3678794
怎么回事?什么使它适当地矢量化?
答案 0 :(得分:5)
实际上,您没有引用定义R函数的代码。在R中我们看到
> dexp
function (x, rate = 1, log = FALSE)
.Call(C_dexp, x, 1/rate, log)
<bytecode: 0x3582cf8>
<environment: namespace:stats>
告诉我们dexp()函数是在stats包中定义的。通过一点挖掘,我们可以在统计数据中看到NAMESPACE
useDynLib(stats, .registration = TRUE, .fixes = "C_")
告诉我们,当暴露给R时,前缀C_被添加到函数名中。所以我们在stats包中寻找一个名为dexp
的C函数。有两个相关条目
init.c:147: CALLDEF_MATH2_1(dexp),
distn.c:144:DEFMATH2_1(dexp)
第一个是使C函数可用于R的宏,第二个是定义函数的宏。对于后者,宏被定义为
#define DEFMATH2_1(name) \
SEXP do_##name(SEXP sa, SEXP sb, SEXP sI) { \
return math2_1(sa, sb, sI, name); \
}
告诉我们要查找math2_1函数,稍微向上一点
static SEXP math2_1(SEXP sa, SEXP sb, SEXP sI, double (*f)(double, double, int))
{
SEXP sy;
R_xlen_t i, ia, ib, n, na, nb;
double ai, bi, *a, *b, *y;
int m_opt;
int naflag;
if (!isNumeric(sa) || !isNumeric(sb))
error(R_MSG_NONNUM_MATH);
SETUP_Math2;
m_opt = asInteger(sI);
mod_iterate(na, nb, ia, ib) {
// if ((i+1) % NINTERRUPT) R_CheckUserInterrupt();
ai = a[ia];
bi = b[ib];
if_NA_Math2_set(y[i], ai, bi)
else {
y[i] = f(ai, bi, m_opt);
if (ISNAN(y[i])) naflag = 1;
}
}
FINISH_Math2;
return sy;
} /* math2_1() */
mod_iterate()
调用实际上是另一个宏
#define mod_iterate(n1,n2,i1,i2) for (i=i1=i2=0; i<n; \
i1 = (++i1 == n1) ? 0 : i1,\
i2 = (++i2 == n2) ? 0 : i2,\
++i)
你可以看到,至少在信仰的眼中,C代码正在实现一个循环,所以原始问题隐含的魔法(紧凑的C代码导致矢量化计算)不存在 - 它是C级的迭代。
math2_1
将最终参数视为一个函数,在本例中是原始问题中引用的dexp
。此函数应用于迭代中的每个元素。