简单来说,函数是用来操作和处理向量、列表或数据帧的方法。这个定义也许不够严谨,但它突出了一个重点,即函数会接收一些输入参数,这些参数可以是向量(甚至只包含一个元素)、列表或数据帧,然后该函数会输出一个结果,通常也是向量、列表或数据帧。
执行文件系统操作及其他特定任务的函数则是例外,在其他语言中,这些例外通常会被称为过程。例如1.5.1节中遇到过的file.create()函数。
R语言最受赞赏的特性之一就是可以很容易地查看其所有可用函数的定义,只需要使用函数的唯一名称作为命令提交即可,无须输入括号。现在,请读者尝试查看mode()函数的定义,代码如下:
mode function (x) { if (is.expression(x)) return("expression") if (is.call(x)) return(switch(deparse(x[[1L]])[1L], `(` = "(", "call")) if (is.name(x)) "name" else switch(tx <- typeof(x), double = , integer = "numeric", closure = , builtin = , special = "function", tx) } <bytecode: 0x102264c98> <environment: namespace:base>
本书不打算深入探讨mode()函数的细节内容,但需要读者注意函数定义的一些结构元素。
● 这里的function()表示mode()函数本身。
● 对mode()函数的唯一参数x进行了明确说明。
● 在function()调用之后的所有内容都用花括号括起来,花括号里面的内容是函数体,它包含对输入参数的所有计算和操作。
在R语言中,上述函数定义的结构元素是定义一个函数所需要的最少元素,可简化表示如下:
function_name <- function(arguments){ [function body] }
既然已经知道了函数定义的原理,现在就定义一个简单的函数,即给任意输入的数字加上2:
adding_two <- function(the_number){ the_number + 2 }
这个函数可行吗?当然可行。 为了测试这个函数,必须先执行声明函数定义的两行代码,然后就可以使用读者的自定义函数了:
adding_two( the_number = 4) [1] 6
继续介绍更加复杂但与函数更相关的概念——函数内的赋值。设想读者正在编写一个函数,并将函数结果存储在一个名为“function_result”的向量中,读者编写的代码可能如下所示:
my_func <- function(x){ function_result <- x / 2 }
假如x的参数值为4,当执行这个函数时,函数的输出应该是一个值为2、名称为“function_result”的对象。
现在请读者根据前面的知识,尝试输出这个对象:
function_result
执行结果为:
Error: object function_result not found
为什么会这样呢?上述错误输出是由函数内赋值的规则所控制的,规则的具体内容如下。
● 函数内部能够查看并使用函数外部定义的变量。
● 函数内部定义的变量只能在函数内部使用。
那么,如何将function_result对象从函数内部输出呢?通常有以下两种方式。
● 使用<<-运算符——通常称之为超赋值运算符。
● 使用assign()函数。
使用超赋值运算符来重新编写上述函数,具体如下:
my_func <- function(x){ function_result <<- x / 2 }
如果试着运行它,会发现function_result对象出现在IDE的变量资源管理器中。需要说明的是,从函数内导出对象和将对象作为函数的返回结果是不一样的,如下面的函数所示:
my_func <- function(x){ function_result <- x / 2 function_result }
如果再次尝试运行my_func(4),控制台会输出如下结果:
[1] 2
但在IDE的变量资源管理器中却找不到function_result对象,这是为什么呢?原因是,在上面的函数中,function_result对象的值成了函数的返回值。不管怎样,与上述第一个超赋值运算符的例子类似,这个对象现在是通过标准赋值运算符定义的。