如前所见,R里面的函数有三种类型,闭包
(SEXPTYPE CLOSXP,, 专用函数(SPECIALSXP)和内置函数
(BUILTINSXP)。在本节,我们研究函数的事实参数在什么时候赋值。
这些规则和内部的(专用的/内置的)以及R-水平的函数(闭包)不一样。
对于一个闭包的调用,一旦事实的和形式上的参数匹配,一个匹配好的
调用(另外一个LANGSXP)会被创建。这个过程首先用对提供的值
创建的允诺列表代替事实参数列表。
然后,它创建一个包含可以和事实上或默认值匹配的形式参数名字的环境:
所有可以匹配的值是允诺,默认值在刚创建环境中作为允诺赋值。
该环境然后用于函数主体的求值。在碰到允诺时,允诺会被强制(因此导致
事实上或默认的参数赋值)。
如果闭包是S3泛型定义(就是,含有一个对UseMethod的调用),
求值过程是一样的直到遇到UseMethod。
此时,被分发到参数(通常是第一个)如果没有准备好,
它将会被赋值。如果一个方法是闭包,
一个新的求值环境会为它而创建。在这个环境中,
该方法中匹配上的参数,在对该泛型方法主体解析
过程中需要用到的变量都会包括在内。
(注意,在调用方法时,对泛型定义的主体中的形式参数的
值得改变会被抛弃的,但被强制的事实参数允诺保留
它们被强制时发现的值。另外一方面,缺损参数
有来自方法默认提供的值,同时也是允诺,但不是来自泛型定义。)
如果方法是专用或内置的,它会用
泛型定义里面的允诺(可能已经强制过的)的匹配上的参数列表。
专用和内置函数的本质差别
1
在于专用函数的参数不会再C代码调用前赋值,但内置函数却可以。
在两种情况下,都采用位置匹配(Positional Matching)的策略。
注意,定义专用/内置函数和定义
原始或.Internal函数是分开的:
function 是一个专用的原始函数,
+ 是内置的原始函数,switch是专用的
.Internal 而 grep 是内置的 .Internal。
许多内部函数都是内部泛型函数,这意味着在特定场合,它们被调用时不会
给它们的参数赋值,但是C代码会开始一个对DispatchOrEval的调用。
后者对第一个参数赋值,并且查找基于它的类的第一个方法。
(如果 S4 分发是打开的,S4方法首先被使用,即使类是S3的类。)
如果它发现一个方法,它分发给该方法一个基于允诺的调用去
给余下的参数赋值。如果没有方法被找到,
余下的参数在返回到内部的泛型函数前被赋值。
另外一种让内部函数泛型化的方法是让函数分到组泛型化里面去。
所有这种类型的函数是内置的(可以马上对它们所有参数赋值),包含
所有对 C 函数 DispatchGeneric 的调用。对于
"Math" 组泛型化,在参数数目上还有一些特别的特征,
一些成员仅允许一个参数,一些运行两个(第二参数有默认值)
但原始成员(通常)允许一个或多个参数泛型化(采用默认方法
时只允许一个参数)。