在R中,如何使函数内的变量可用于该函数内的较低级别的函数?(with,attach,environment)
Posted
技术标签:
【中文标题】在R中,如何使函数内的变量可用于该函数内的较低级别的函数?(with,attach,environment)【英文标题】:In R, how to make the variables inside a function available to the lower level function inside this function?(with, attach, environment) 【发布时间】:2013-01-02 04:19:13 【问题描述】:更新 2 @G。 Grothendieck 发布了两种方法。第二个是改变函数内部的函数环境。这解决了我编码重复过多的问题。我不确定这是否是在将我的脚本放入包时通过 CRAN 检查的好方法。等我有结论再更新。
更新
我试图将大量输入参数变量传递给f2
,并且不想将函数内的每个变量都索引为env$c, env$d, env$calls
,这就是为什么我尝试在f5
和@ 中使用with
987654325@(修改后的f2
)。但是,assign
不适用于 with
在 内,将
assign
移到 with
外将完成这项工作,但在我的实际情况下,我在 with
表达式中有几个 assign
s我不知道如何轻松地将它们移出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()
函数f2
在f1
内部,当调用f2
时,它会在环境environment(f1)
中查找变量calls,c,d
。这就是我想要的。
但是,当我想在其他函数中也使用f2
时,我将在全局环境中定义此函数,将其命名为f4
。
f4 <- function(P)
assign("calls", calls+1, inherits=TRUE)
print(calls)
return(P+c+d)
这不起作用,因为它将在全局环境中而不是在调用该函数的函数内部查找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()
安全的方式应该是在f4
的输入参数中定义calls,c,d
,然后将这些参数传递给f4
。但是,在我的情况下,有太多变量要传递给这个函数f4
,我可以将它作为环境传递并告诉f4
不要查看全局环境(environment(f4)
) ,仅在调用 f3
时查看 environment
内部。
我现在解决的方法是将环境作为列表,使用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()
但是,现在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()
我不确定这应该如何在 R 中完成。我的方向是否正确,尤其是在通过 CRAN 检查时?有人对此有一些提示吗?
【问题讨论】:
【参考方案1】:(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()
(2) 重置被调用函数的环境 我们可以在f1b
中重置f2b
的环境,如下所示:
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()
(3) 使用 eval.parent(substitute(...)) 的宏 另一种方法是定义一个类似宏的构造,该构造有效地将 f2c
内联的主体注入到 @987654326 @。这里f2c
与f2b
相同,除了calls <- calls + 1
行(不需要<<-
)和整个主体包裹在eval.parent(substitute(...))
中。 f1c
与 f1a
相同,只是对 f2a
的调用被替换为对 f2c
的调用。
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()
(4) defmacro 这和上一个解决方案几乎相同,只是它使用 gtools 包中的defmacro
来定义宏,而不是自己做。 (另请参阅 Rcmdr 包以获取另一个 defmacro 版本。)由于 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()
【讨论】:
这给出了我想要的结果,但我没有写清楚我的要求。事实上,我想避免索引所有传入f2
的变量,即不写env$
,这就是我尝试使用with
的原因。我希望能够修改外部calls
内部f2
但不更改当前环境中的其他变量。我会更新问题。
看起来正是我所需要的。我将用我的真实案例对其进行测试并稍后更新。非常感谢您的帮助!【参考方案2】:
每当我使用嵌套函数并且不将变量作为参数传递,而是使用...
传递它们时,我都会在所有嵌套函数中使用以下函数从父环境中获取变量。
LoadVars <- function(variables, ...)
for (var in 1:length(variables))
v <- get(variables[var], envir = parent.frame(n=2))
assign(variables[var], v, envir = parent.frame(n=1))
在一个嵌套函数中,我然后LoadVars(c("foo", "bar"))
。
这种方法很有用,因为您只传递所需的变量,类似于通过参数传递变量。
方法 2
但是,重写此函数以从父函数加载 所有 变量很简单——如果需要,可以更高,只需将 parent.frame
中的 n
值从其原始值增加2
.
LoadVars <- function()
variables <- ls(envir = parent.frame(n=2))
for (var in 1:length(variables))
v <- get(variables[var], envir = parent.frame(n=2))
assign(variables[var], v, envir = parent.frame(n=1))
示例
a <- 1
A <- function(...)
b <- 2
printf("A, a = %s", a)
printf("A, b = %s", b)
B()
B <- function(...)
LoadVars()
printf("B, a = %s", a)
printf("B, b = %s", b)
A()
如果您不在B
中加载变量,那么B
可以加载a
,因为它是一个全局环境变量,而不是位于A()
中的b
。
输出:
[1] "A, a = 1"
[1] "A, b = 2"
[1] "B, a = 1"
[1] "B, b = 2"
【讨论】:
【参考方案3】:也可以使用一个函数来重新定义指定环境中的其他函数。
test_var <- "global"
get_test_var <- function()
return(test_var)
some_function <- function()
test_var <- "local"
return(get_test_var())
some_function() # Returns "global". Not what we want here...
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
some_function2 <- function()
test_var <- "local"
# define function locally
get_test_var2 <- function()
return(test_var)
return(get_test_var2())
some_function2() # Returns "local", but 'get_test_var2' can't be used in other places.
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
add_function_to_envir <- function(my_function_name, to_envir)
script_text <- capture.output(eval(parse(text = my_function_name)))
script_text[1] <- paste0(my_function_name, " <- ", script_text[1])
eval(parse(text = script_text), envir = to_envir)
some_function3 <- function()
test_var <- "local"
add_function_to_envir("get_test_var", environment())
return(get_test_var())
some_function3() # Returns "local" and we can use 'get_test_var' from anywhere.
这里add_function_to_envir(my_function_name, to_envir)
捕获函数的脚本,在新环境中对其进行解析和重新评估。
注意:my_function_name
的函数名称需要用引号引起来。
【讨论】:
【参考方案4】:一般来说,我会说函数内部需要的任何变量都应该通过它的参数传递。此外,如果以后需要它的值,您可以从函数中将其传回。不这样做会很快导致奇怪的结果,例如如果有多个函数定义一个变量x
,应该使用哪一个。如果变量的数量较大,您可以为其创建自定义数据结构,例如将它们放入命名列表中。
【讨论】:
我总体上同意你的@Paul。我正在尝试使我的代码成为 R 包,但无法轻松通过 CRAN 检查,并出现许多警告,例如全局变量绑定。有很多重复的代码,因为f2
是在函数内部定义的,我想在另一个新函数中使用它。我意识到复制粘贴不是一个好的选择,并且可能会在以后的步骤中导致问题。我也想减少工作量,因为我不想过多地更改已经存在的脚本。这就是为什么我尝试传递环境而不是定义新的数据结构。以上是关于在R中,如何使函数内的变量可用于该函数内的较低级别的函数?(with,attach,environment)的主要内容,如果未能解决你的问题,请参考以下文章