繁体   English   中英

有两个功能访问单个功能的环境的正确方法?

[英]Proper way to have two functions access a single function's environment?

根据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

更简洁的方法是使用面向对象的方法。 已经有一个使用参考类的答案。

使用类的典型面向对象方法将创建一个类,然后创建一个单例对象,即该类的单个对象。 当然创建一个类只是为了从它创建一个对象有点浪费,所以这里我们提供一个proto示例。 (创建一个包含count的函数和执行实际工作的函数也有类似的问题 - 你创建一个封闭函数只运行一次。)proto模型允许一个人创建一个对象,直接绕过创建一个类的需要使用一次。 这里foobar是具有属性count和方法statsstatfoostatbar的proto对象。 请注意,我们考虑了stats以避免在statfoostatbar重复其代码。 (继续向下)

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

第二种设计是将statfoostatbar作为独立函数,只保留foobar countstats (继续向下)

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)

您可以使用这样的引用类:

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)

这使得相对清楚的是, statfoostatbar是纯函数。

另一个简单的选择是创建一个环境并将其分配给两个函数。 这里我使用更简单的函数用于说明目的,但这可以很容易地扩展:

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"

两个功能都具有相同的环境。

您可以摆脱工厂功能,更明确地使用环境。 像这样的解决方案也可以

.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变量是通过立即执行匿名函数来获取其环境而创建的。 然后,我们从环境本身提取函数以修改其值。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM