簡體   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