R语言 抽象
已经使用R语言一段时间的人,很可能已经习惯于将功能作为参数传递给其他函数。然而,人们不太可能从他们个人的自定义代码中返回函数。这实在是太可怕了,因为这样做可以开辟一个全新的抽象国际,可以大大降低完成特定风格的职责所需的代码数量和复杂性。在这里,我们提供一些简短的例子,说明R程序员如何利用词法闭包来封装记录和策略。
在R中的实现
首先,一个简单的例子,假设你想要一个函数,为其参数提供 add_2() 。你可能可以这样写。
add_2 <- function(y) { 2 + y }
这正是你所期望的。
> add_2(1:10)
[1] 3 4 5 6 7 8 9 10 11 12
现在,假设你需要其他每一个特征,而不是提供7给它的参数。但这将是非常低效的:如果将来你发现你犯了一个错误,而且你确实想把这些数值相乘而不是相加,你将被迫在一些地方交换代码。在这个微不足道的例子中,这不会有很多麻烦,但对于更复杂的项目,重复代码是一个灾难的秘诀。一个更高的概念是写一个特征,它需要一个参数,x,返回每一个其他的函数,提供它的参数,y,到x。
add_x <- function(x) {
function(y) { x + y }
}
现在,当你用一个参数命名 add_x 时,你可能会得到一个功能,而这个功能正是你需要的。
add_2 <- add_x(2)
add_7 <- add_x(7)
> add_2(1:10)
[1] 3 4 5 6 7 8 9 10 11 12
> add_7(1:10)
[1] 8 9 10 11 12 13 14 15 16 17
所以这似乎并不太令人震惊。但是如果你仔细看 add_x 的定义 , 你可能会注意到一些奇怪的东西:当它被提到以后的时候,返回的特征是如何实现发现x的?
事实证明,R是词义范围的,这意味着特征与它们所描述的环境有联系。在这种情况下,当你调用 add_x 时,你提供的x参数会被附加到返回特性的环境中。在不同的短语中,在这个简单的例子中,你可以认为R只是简单地改变了特征中x变量的所有实例,使其降低到你在调用 add_x 时指定的值。好吧,这可能是一个巧妙的技巧,但是,如何才能更有效地使用它?对于一个稍微复杂的例子,想想你正在做一些复杂的引导,为了提高效率,你预先分配容器向量来保存结果。如果你只有一个单一的效果向量,这很容易–你需要做的就是考虑到每当你向向量上传一个最终结果时,都要迭代一个索引计数器。
for (i in 1:nboot) {
bootmeans[i] <- mean(sample(data, length(data),
replace = TRUE))
}
> mean(data)
[1] 0.0196
> mean(bootmeans)
[1] 0.0188
但是,想想你需要跟踪几个非常规的统计数据,每一个都需要你保持跟踪一个独特的索引变量。如果你的bootstrapping ordinary有一点点复杂,这可能会很乏味,而且容易出错。通过使用闭包,你可以总结出所有这些簿记。下面是一个构造函数,它封装了一个预先分配的容器向量。
make_container <- function(n) {
x <- numeric(n)
i <- 1
function(value = NULL) {
if (is.null(value)) {
return(x)
}
else {
x[i] <<- value
i <<- i + 1
}
}
}
当你用一个问题调用 make_container 时,它会预先分配一个指定周期的数字向量n,并返回一个特征,允许你对该向量进行特征统计,而不必担心大约保持索引的音乐。如果你不该返回特征的参数是NULL,那么整个向量就是后面的。
bootmeans <- make_container(nboot)
for (i in 1:nboot)
bootmeans(mean(sample(data, length(data),
replace = TRUE)))
> mean(data)
[1] 0.0196
> mean(bootmeans())
[1] 0.0207
在这里, make_container 是非常简单的,但它可以根据你的需要而变得复杂。例如,你可能想让构造函数进行一些昂贵的计算,而这些计算你可以不再在每次角色被称为时进行。实际上,这就是我在 boolean3 软件包中完成的,以减少每次新发布的优化习惯所进行的计算范围。