简体   繁体   English

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

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

Based on the answer provided in 1088639 , I set up a pair of functions which both access the same sub-function's environment. 根据1088639中提供的答案,我设置了一对函数,它们都访问相同的子函数环境。 This example works, but I wanted to see if I'd missed some cleaner way to "connect" both top-level functions to the internal environment. 这个例子有效,但我想看看我是否错过了将两个顶层函数“连接”到内部环境的更简洁方法。

( Back story: I wanted to write a pair of complementary functions which shared a variable, eg "count" in this example, and meet CRAN package requirements which do not allow functions to modify the global environment. ) (背景故事:我想编写一对共享变量的互补函数,例如本例中的“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)
}

Sample output: 样本输出:

> 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

A cleaner method would be to use an object oriented approach. 更简洁的方法是使用面向对象的方法。 There is already an answer using reference classes. 已经有一个使用参考类的答案。

A typical object oriented approach with classes would create a class and then create a singleton object, ie a single object of that class. 使用类的典型面向对象方法将创建一个类,然后创建一个单例对象,即该类的单个对象。 Of course it is a bit wasteful to create a class only to create one object from it so here we provide a proto example. 当然创建一个类只是为了从它创建一个对象有点浪费,所以这里我们提供一个proto示例。 (Creating a function to enclose count and the function doing the real work has a similar problem -- you create an enclosing function only to run it once.) The proto model allows one to create an object directly bypassing the need to create a class only to use it once. (创建一个包含count的函数和执行实际工作的函数也有类似的问题 - 你创建一个封闭函数只运行一次。)proto模型允许一个人创建一个对象,直接绕过创建一个类的需要使用一次。 Here foobar is the proto object with property count and methods stats , statfoo and statbar . 这里foobar是具有属性count和方法statsstatfoostatbar的proto对象。 Note that we factored out stats to avoid duplicating its code in each of statfoo and statbar . 请注意,我们考虑了stats以避免在statfoostatbar重复其代码。 (continued further down) (继续向下)

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)

giving: 赠送:

> 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

A second design would be to have statfoo and statbar as independent functions and only keep count and stats in foobar (continued further down) 第二种设计是将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)

giving similar output to the prior example. 给前面的例子提供类似的输出。

Third approach Of course the second variation could easily be implemented by using local and a function getting us close to where you started. 第三种方法当然,第二种变体可以通过使用local和函数轻松实现,使我们接近您开始的位置。 This does not use any packages but does not create a function only to throw it away: 这不会使用任何包,但不会创建一个函数只是扔掉它:

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)

You can use reference class like this: 您可以使用这样的引用类:

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)

It makes it relatively clear that neither statfoo nor statbar is a pure function. 这使得相对清楚的是, statfoostatbar是纯函数。

Another simple option is tocreate an environment and assign it to both functions. 另一个简单的选择是创建一个环境并将其分配给两个函数。 Here I use simpler functions for illustrative purposes, but this can be easily extended: 这里我使用更简单的函数用于说明目的,但这可以很容易地扩展:

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))

Then: 然后:

> f1()
[1] "hello 1"
> f2()
[1] "goodbye 2"
> f1()
[1] "hello 3"

Both functions have the same environment. 两个功能都具有相同的环境。

You could get rid of the factory functions, and more explicitly use environments. 您可以摆脱工厂功能,更明确地使用环境。 A solution like this would work as well 像这样的解决方案也可以

.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)
}

The .env variable is created by immediately executing an anonymous function to get its environment. .env变量是通过立即执行匿名函数来获取其环境而创建的。 We then extract the function from the environment itself to modify its values. 然后,我们从环境本身提取函数以修改其值。

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

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