大家好,今天给大家分享下R语言中自定义函数的知识点,后续将陆续推出R语言各个模块的知识点:
1 一个例子
例子:
2 函数的组成部分1)  函数体:body(),函数的代码
2)  形式参数列表:formals(),控制函数调用的参数列表
3)  环境:environment(),函数的变量所在位置的“地图”
注意:函数都有上述三个组成部分,有一个例外:原语函数(如:sum等),它是直接用.Primitive()调用C语言代码,并且不包含R语言代码。因此它们的formals()、body()、environment()都是NULL
3 词法作用域1)概论:        词法作用域进行符号的值的查找,是基于在函数创建时是如何嵌套的,而不是它们
在调用时如何嵌套的。 有了词法作用域,你不需要知道函数是怎么被调用的,以及在哪里查找变量的值。你只需要看看函数的定义即可。
 
2)四个基本原则:   名字屏蔽、函数和变量、全新的开始状态、动态查找
 
(1)名字屏蔽: a)如果一个名字在函数中没有定义,那么R语言将向上一个层次查找。
   b)如果一个函数内部定义了另一个函数,也适用同样的规则: 首先,查看当前函数的内部,然后是这个函数被定义的环境,然后继续向上,以此类推,一直到全局环境,然后,再查找其它已经加载的包
   c)同样的规则也适用于闭包——由其它函数创建的函数
 
例子:
(2)函数和变量对于函数,规则有一点点调整。 如果很明显你要的是函数(例如,f(3)),那么在这样的语境中,R语言在搜索时,将忽略那些不是函数的对象。
(3)全新的开始状态
         函数每次调用时,一个新的环境就会被创建出来,随后函数会在该环境中执行。函数无法报告它上一次被调用时发生了什么,因为每次调用都是完全独立的。
(4)动态查找
词法作用域决定了去哪里查找值,而不是决定在什么时候查找值。 R语言在函数运行时查找值,而不是在函数创建时查找值。这意味着,函数的输出是可以随着它所处的环境外面的对象,而发生变化的。
4 所有的操作都是函数调用   万事万物都是对象;发生的所有事情都是函数调用。
 
一个更有用的应用是把lapply()或sapply与取子集操作组合起来:以下两个等价5 函数参数1)通过位置、全名来设定参数
当调用一个函数时,你可以通过参数的位置,或者通过完整的名称或者部分的名称,来匹配参数。 参数匹配的顺序是:首先是精确的名称匹配(完美匹配),然后通过前缀匹配,最后通过位置匹配。
一般来说,你只希望使用位置匹配来匹配排在前面的一、两个参数;它们是最常用的,大多数用户都知道它们是什么。要避免使用位置匹配来匹配较少使用的参数,并且仅使用具有可读性的缩写形式来对参数进行部分匹配。 (如果你正在为一个包编写代码,你想要把它发布在CRAN上,那么不能使用部分匹配,而必须使用完整的名字。) 命名的参数应该总是排在未命名的参数后面。 如果一个函数使用了...,则...之后列出的参数都必须使用它们的全名。
 
2)给定一个参数列表来调用函数
3)默认参数与缺失参数(a)函数定义的时候设定初始值
(b)默认参数由另一个参数创建
(c)默认参数在函数内部创建的变量来定义
默认参数在函数内部进行计算。 这意味着,如果表达式依赖于当前环境,那么结果将是变化的,取决于你是否使用了默认值或显式地提供了一个值。
4)延迟计算默认情况下,R语言函数的参数是延迟计算的。仅当实际用到这些参数的时候,它们才会被计算出来。
延迟性在if中的作用:
以下的第二个语句将仅在第一个语句为TRUE时才会被计算。如果不是这样的话,那么该语句会返回一个错误,因为NULL > 0是一个长度为0的逻辑向量,而不是if的一个有效输入。
6 特殊调用1)中缀函数大多数R中的函数是前缀操作符:函数的名称排在参数的前面。你还可以创建中缀函数,函数名位于它的参数之间,比如+或-。所有用户创建的中缀函数都必须以%开始和结束,R预定义了这些中缀函数:%%、%*%、%/%、%in%、%o%、%x%。
不需要%的内置中缀操作符的完整列表为: 
::, :::, $, @, ?, *, /, +, -, >, >=, <, <=, ==, !=, !,&, &&, |, ||, ~, <-, <<-
中缀函数的名字比普通R函数更加灵活:它们可以包含任何字符序列(当然,除了%以外)。 你需要在定义函数的时候,对任何特殊字符进行转义,而不是在调用的时候进行转义
2)替换函数
替换函数的行为表现得好像它们可以就地修改(译者注:modify in place,即修改立即生效,直接作用在被修改的对象上)参数,并且它们都拥有特别的名字xxx<-。 它们通常有两个参数(x和值),虽然它们可以有更多参数,但是它们必须返回修改过的对象。 例如,下面的函数允许你修改向量的第二个元素:
当R计算赋值语句second(x) <- 5时,它注意到左手边的<-不是一个简单的名称,因此它寻找一个命名为second<-的函数来进行替换操作。
 
注意:我之所以说它们"表现得好像"可以就地修改参数,是因为实际上它们创建了一个修改后的副本。 我们可以使用pryr::address()来查看,找到底层对象的内存地址。
把替换和取子集操作结合起来:
7 返回值1)语法:retrun()语句
f <-function(x, y) { 
if (!x) return(y) 
# 这里是复杂的处理过程 
}
 
2)保护机制
R语言提供了保护机制,让你避免一类副作用:大多数R对象具有修改时复制(copy-on-modify)的语义。所以修改函数参数不会改变原始值:
3)返回不可见的值invisible(x)
强制可见:(a<-2)
 
8 退出时执行  on.exit()
 
注意:如果你在一个函数中调用多个on.exit()函数,那么请务必设置add = TRUE。 不幸的是,在on.exit()中add的默认值是add = FALSE,这样每次你运行它的时候,它都会覆盖已有的退出(exit)表达式。 由于on.exit()的特殊实现方式,因此无法创建一个默认设置为add = TRUE的变种函数,所以使用它的时候必须小心。