根据1088639中提供的答案,我设置了一对函数,它们都访问相同的子函数环境。这个例子有效,但我想看看我是否错过了将顶层函数“连接”到内部环境的更简洁方法。
(背景故事:我想编写一对共享变量的互补函数,例如本例中的“count”,和符合CRAN包要求,这些要求不允许函数修改全局环境。)
static.f <- function() {
count <- 0
f <- function(x) {
count <<- count + 1
return( list(mean=mean(x), count=count) )
}
return( f )
}
# make sure not to delete this command, even tho' it's not
# creating a function.
f1 <- static.f()
statfoo <- function(x){
tmp<-f1(x)
tmp<- list(tmp,plus=2)
return(tmp)
}
statbar <- function(x){
tmp<-f1(x)
tmp<- list(tmp,minus=3)
return(tmp)
}
示例输出:
> statfoo(5)
[[1]]
[[1]]$mean
[1] 5
[[1]]$count
[1] 1
$plus
[1] 2
Rgames> statfoo(5)
[[1]]
[[1]]$mean
[1] 5
[[1]]$count
[1] 2
$plus
[1] 2
> statbar(4)
[[1]]
[[1]]$mean
[1] 4
[[1]]$count
[1] 3
$minus
[1] 3
> statfoo(5)
[[1]]
[[1]]$mean
[1] 5
[[1]]$count
[1] 4
$plus
[1] 2
答案 0 :(得分:5)
更干净的方法是使用面向对象的方法。已经有一个使用参考类的答案。
使用类的典型面向对象方法将创建一个类,然后创建一个单例对象,即该类的单个对象。当然创建一个类只是为了从它创建一个对象有点浪费,所以这里我们提供一个proto示例。 (创建一个包含count
的函数和执行实际工作的函数有一个类似的问题 - 你创建一个封闭函数只运行一次。)proto模型允许一个人创建一个对象直接绕过需要创建一个类只使用一次。此处foobar
是具有属性count
和方法stats
,statfoo
和statbar
的proto对象。请注意,我们考虑了stats
以避免在statfoo
和statbar
中的每一个中重复其代码。 (继续向下)
library(proto)
foobar <- proto(count = 0,
stats = function(., x) {
.$count <- .$count + 1
list(mean = mean(x), count = .$count)
},
statfoo = function(., x) c(.$stats(x), plus = 2),
statbar = function(., x) c(.$stats(x), plus = 3)
)
foobar$statfoo(1:3)
foobar$statbar(2:4)
,并提供:
> foobar$statfoo(1:3)
$mean
[1] 2
$count
[1] 1
$plus
[1] 2
> foobar$statbar(2:4)
$mean
[1] 3
$count
[1] 2
$plus
[1] 3
第二个设计是将statfoo
和statbar
作为独立函数,并且只在count
中保留stats
和foobar
(继续向下)
library(proto)
foobar <- proto(count = 0,
stats = function(., x) {
.$count <- .$count + 1
list(mean = mean(x), count = .$count)
}
)
statfoo <- function(x) c(foobar$stats(x), plus = 2)
statbar <- function(x) c(foobar$stats(x), plus = 3)
statfoo(1:3)
statbar(2:4)
给出前面例子的类似输出。
第三种方法当然,第二种变体可以通过使用local
和一个让我们接近你开始的函数轻松实现。这不会使用任何包,但不会创建一个函数只是扔掉它:
foobar <- local({
count <- 0
function(x) {
count <<- count + 1
list(mean = mean(x), count = count)
}
})
statfoo <- function(x) c(foobar(x), plus = 2)
statbar <- function(x) c(foobar(x), plus = 3)
statfoo(1:3)
statbar(2:4)
答案 1 :(得分:4)
您可以使用这样的引用类:
foobar <- setRefClass(
'foobar',
fields = list(count='numeric'),
methods = list(
initialize=function() {
.self$initFields(count = 0L)
},
statfoo = function(x) {
count <<- count + 1L
list(list(mean=mean(x), count=count), plus=2)
},
statbar = function(x){
count <<- count + 1L
list(list(mean=mean(x), count=count), minus=3)
}
)
)()
foobar$statfoo(5)
foobar$statbar(3)
相对清楚的是,statfoo
和statbar
都不是纯函数。
答案 2 :(得分:4)
另一个简单的选择是创建一个环境并将其分配给两个函数。这里我使用更简单的函数用于说明目的,但这可以很容易地扩展:
f1 <- function() {count <<- count + 1; return(paste("hello", count))}
f2 <- function() {count <<- count + 1; return(paste("goodbye", count))}
environment(f1) <- environment(f2) <- list2env(list(count=0))
然后:
> f1()
[1] "hello 1"
> f2()
[1] "goodbye 2"
> f1()
[1] "hello 3"
两个功能都具有相同的环境。
答案 3 :(得分:3)
您可以摆脱工厂功能,更明确地使用环境。像这样的解决方案也可以使用
.env<-(function() {
count <- 0
f <- function(x) {
count <<- count + 1
return( list(mean=mean(x), count=count))
}
return(environment())
})()
statfoo <- function(x){
list(.env$f(x),plus=2)
}
statbar <- function(x){
list(.env$f(x),minus=3)
}
.env
变量是通过立即执行匿名函数来获取其环境而创建的。然后,我们从环境本身提取函数以修改其值。