在R中,如何使函数内的变量可用于此函数内的较低级函数?(with,attach,environment)

Zhe*_*lei 24 r function environment-variables with-statement assign

更新2 @G.格洛腾迪克发布了两种方法.第二个是改变函数内的函数环境.这解决了我的编码重复过多的问题.在将脚本编写成包时,我不确定这是否是通过CRAN检查的好方法.当我得出一些结论时,我会再次更新.

更新

我试图传递很多输入参数变量,f2并且不想索引函数内的每个变量env$c, env$d, env$calls,这就是我尝试使用within f5f6(修改过f2)的原因.但是,assign不能在with里面工作{},移动assign到外面with会做的工作,但在我的实际情况下,我有几个assigns在with表达式内,我不知道如何with轻松地将它们移出功能.

这是一个例子:

## In the <environment: R_GlobalEnv>
a <- 1
b <- 2
f1 <- function(){
    c <- 3
d <- 4
f2 <- function(P){
    assign("calls", calls+1, inherits=TRUE)
    print(calls)
    return(P+c+d)
 }
calls <- 0
v <- vector()
for(i in 1:10){
    v[i] <- f2(P=0)
    c <- c+1
    d <- d+1
  }
 return(v)
}
f1()
Run Code Online (Sandbox Code Playgroud)

函数f2在内部f1,在f2调用时,它calls,c,d在环境中查找变量environment(f1).这就是我想要的.

但是,当我想f2在其他函数中使用时,我将在Global环境中定义此函数,而不是调用它f4.

f4 <- function(P){
  assign("calls", calls+1, inherits=TRUE)
  print(calls)
  return(P+c+d)
}
Run Code Online (Sandbox Code Playgroud)

这不起作用,因为它将calls,c,d在全局环境中查找,而不是在调用函数的函数内部查找.例如:

f3 <- function(){
  c <- 3
  d <- 4
  calls <- 0
  v <- vector()
  for(i in 1:10){
    v[i] <- f4(P=0) ## or replace here with f5(P=0)
    c <- c+1
    d <- d+1
  }
  return(v)
}
f3()
Run Code Online (Sandbox Code Playgroud)

应该calls,c,d在输入参数中定义安全方式f4,然后将这些参数传递给f4.但是,在我的情况下,有太多的变量要传递给这个函数f4,最好我可以将它作为一个环境传递并告诉f4不要查看Global环境(environment(f4)),只看看调用environmentwhen时f3.

我现在解决它的方法是将环境用作列表并使用该with函数.

f5 <- function(P,liste){
  with(liste,{
     assign("calls", calls+1, inherits=TRUE)
     print(calls)
     return(P+c+d)
     }
  )
}
f3 <- function(){
  c <- 3
  d <- 4
  calls <- 0
  v <- vector()
  for(i in 1:10){
    v[i] <- f5(P=0,as.list(environment())) ## or replace here with f5(P=0)
    c <- c+1
    d <- d+1
  }
  return(v)
}
f3()
Run Code Online (Sandbox Code Playgroud)

但是,现在assign("calls", calls+1, inherits=TRUE)不能正常工作,因为assign不会修改原始对象.变量calls连接到目标函数所在的优化函数f5.这就是我使用assign而不是calls作为输入参数传递的原因.使用attach对我来说也不清楚.这是我纠正assign问题的方法:

f7 <- function(P,calls,liste){
  ##calls <<- calls+1
  ##browser()
  assign("calls", calls+1, inherits=TRUE,envir = sys.frame(-1))
  print(calls)
  with(liste,{
    print(paste('with the listed envrionment, calls=',calls))
    return(P+c+d)
  }
  )
}
########
##################
f8 <- function(){
  c <- 3
  d <- 4
  calls <- 0
  v <- vector()
  for(i in 1:10){
    ##browser()
    ##v[i] <- f4(P=0) ## or replace here with f5(P=0)
    v[i] <- f7(P=0,calls,liste=as.list(environment()))
    c <- c+1
    d <- d+1
  }
  f7(P=0,calls,liste=as.list(environment()))
  print(paste('final call number',calls))
  return(v)
}
f8()
Run Code Online (Sandbox Code Playgroud)

我不确定如何在R中做到这一点.我正朝着正确的方向前进,特别是在通过CRAN检查时?有人对此有一些暗示吗?

G. *_*eck 25

(1)通过来电者的环境.您可以将父环境和索引显式传递给它.试试这个:

f2a <- function(P, env = parent.frame()) {
    env$calls <- env$calls + 1
    print(env$calls)
    return(P + env$c + env$d)
}

a <- 1
b <- 2
# same as f1 except f2 removed and call to f2 replaced with call to f2a
f1a <- function(){
    c <- 3
    d <- 4
    calls <- 0
    v <- vector()
    for(i in 1:10){
        v[i] <- f2a(P=0)
        c <- c+1
        d <- d+1
      }
     return(v)
}
f1a()
Run Code Online (Sandbox Code Playgroud)

(2)复位调用的函数的环境是复位的环境f2bf1b这里,如下所示:

f2b <- function(P) {
    calls <<- calls + 1
    print(calls)
    return(P + c + d)
}

a <- 1
b <- 2
# same as f1 except f2 removed, call to f2 replaced with call to f2b
#  and line marked ## at the beginning is new
f1b <- function(){
    environment(f2b) <- environment() ##
    c <- 3
    d <- 4
    calls <- 0
    v <- vector()
    for(i in 1:10){
        v[i] <- f2b(P=0)
        c <- c+1
        d <- d+1
      }
     return(v)
}
f1b()
Run Code Online (Sandbox Code Playgroud)

(3)宏使用eval.parent(替换(...)) 另一种方法是定义一个类似宏的构造,它有效地注入f2c内联体f1c1.除了线(不需要)和整个身体的包裹之外,这里f2c是相同的. 除了调用替换为调用之外,它是相同的. f2bcalls <- calls + 1<<-eval.parent(substitute({...}))f1cf1af2af2c

f2c <- function(P) eval.parent(substitute({
    calls <- calls + 1
    print(calls)
    return(P + c + d)
}))

a <- 1
b <- 2
f1c <- function(){
    c <- 3
    d <- 4
    calls <- 0
    v <- vector()
    for(i in 1:10){
        v[i] <- f2c(P=0)
        c <- c+1
        d <- d+1
      }
     return(v)
}
f1c()
Run Code Online (Sandbox Code Playgroud)

(4)defmacro 这几乎与上一个解决方案相同,只是它defmacro在gtools包中用来定义宏而不是自己做.(另请参阅另一个defmacro版本的Rcmdr包.)由于工作方式defmacro我们也必须通过,calls但由于它是一个宏而不是一个函数,这只是告诉它替换calls,并且与传递calls给函数不同.

library(gtools)

f2d <- defmacro(P, calls, expr = {
    calls <- calls + 1
    print(calls)
    return(P + c + d)
})

a <- 1
b <- 2
f1d <- function(){
    c <- 3
    d <- 4
    calls <- 0
    v <- vector()
    for(i in 1:10){
        v[i] <- f2d(P=0, calls)
        c <- c+1
        d <- d+1
      }
     return(v)
}
f1d()
Run Code Online (Sandbox Code Playgroud)