1.5 编写自定义函数
大致概括一下我们上面所说的内容。我们已经写好一段用来模拟掷一对骰子的R程序,如下所示。
die <-1:6
dice <- sample(die, size = 2, replace = TRUE)
sum(dice)
当你想重新掷骰子时,重新将这一段代码键入R的控制台即可。但是,这种运行代码的方式颇为笨拙。如果能将这段代码打包成一个函数,运行起来会更加方便。我们现在就要尝试写一个这样的函数。我们所要写的函数叫作roll,你可以用它来掷虚拟的骰子。这个函数写成之后,它的运行方式如下:每次调用函数roll(), R都会返回两个骰子点数的和。
roll() ## 8 roll() ## 3 roll() ## 7
R函数可能看起来神秘或炫酷,但其实它们就是一种不同类型的R对象而已。它们包含的不是数据,而是代码。该代码以一种特殊的格式存储起来,以方便在新的场合使用该代码。你可以通过重新创建这种格式来编写自己的函数。
函数构建器
任何一个R函数都包含三个部分:函数名、程序主体以及参数集合。在编写自定义R函数时,你需要定义好这三个部分的内容并将它们各自存储在一个R对象之中。这里要用到function这个函数。具体的做法是调用function()函数,并在其后加上一对大括号{},如下所示。
my function <- function() {}
function的作用是将大括号中的所有代码构建成一个函数。例如,若要将掷骰子的模拟构建成为一个函数,可以调用如下的函数。
roll <- function() { die <-1:6 ➊ dice <- sample(die, size = 2, replace = TRUE) sum(dice) }
➊ 请注意大括号内部的每一行代码都做了缩进操作。这样做的目的是提高代码的可读性,并不会影响代码的运行。对于一整条语句,R会忽略所有的空格和换行符,并一次性运行整条语句。
在左大括号{之后,在每一行结束时按回车键就可以跳入下一行的编辑。R会等待你最后键入右大括号},这样这个函数就构建完成了。
利用function构建完函数之后,不要忘记将其输出保存到某个R对象中。这个对象就是构建的新函数了。它的使用方法很简单,只需要键入这个对象的名称,跟上一对括号即可。
roll()
## 9
你可以将这一对括号想象成这个函数的“触发器”,它可以触发R去运行这个函数。如果你在R控制台中键入一个函数名而没有带上这对括号,那么R只会展示这个函数所存储的代码。如果你带上这对括号,R就会运行这个函数所存储的代码。
roll
## function() {
## die <-1:6
## dice <- sample(die, size = 2, replace = TRUE)
## sum(dice)
## }
roll()
## 6
你在函数中所编写的位于大括号内的内容叫作函数的主体。当运行一个函数时,R会顺序执行该函数主体中的所有代码,并且返回最后一行代码的运行结果。如果最后一行代码不返回任何值,整个函数也不会返回任何值。因此,在编写函数时应该确保最后一行代码具有明确的返回值。检验的方法就是在函数主体内逐行检查,试想每一行代码在命令行中的运行结果是什么。R会不会在最后一行之后显示某个结果?
执行下面的代码会显示某个结果。
dice 1 + 1 sqrt(2)
而执行下面的代码则不会显示任何结果。
dice <- sample(die, size = 2, replace = TRUE)
two <-1 + 1
a <- sqrt(2)
你注意到其中的模式了吗?后一种情形的代码没有在命令行中输出显示任何结果,而是将值存储到某个对象中。