简体   繁体   English

如何从调用环境而不是封闭环境中获取 R 函数的默认值

[英]How to get an R function's default values from the calling environment, not the enclosing environment

R assign() within function fails when nesting source()-ed file, but assign() on its own works.嵌套 source()-ed 文件时,函数内的 Rassign() 失败,但 assign() 自行工作。

My group has many R programs that call other programs.我的小组有许多调用其他程序的 R 程序。 We've created a getObjectFromCalledProgram() function to use with source(..., local = calledProgram <- new.env()) to select specific objects from called programs.我们创建了一个 getObjectFromCalledProgram() 函数与 source(..., local = calledProgram <- new.env()) 一起使用以从被调用程序中选择特定对象。 I've just found that this does not work when sourced programs are nested within each other.我刚刚发现当源程序相互嵌套时这不起作用。 I'd like to understand whether I can alter our getObjectFromCalledProgram() function rather than finding and editing all of the programs in which we've used this approach.我想了解是否可以更改我们的 getObjectFromCalledProgram() 函数,而不是查找和编辑我们使用此方法的所有程序。

Can someone explain why our getObjectFromCalledProgram() function, which very simply uses the assign() function, fails within a source()-ed program, where the assign function, used directly, works?有人能解释一下为什么我们的 getObjectFromCalledProgram() 函数,它非常简单地使用了assign() 函数,但在 source()-ed 程序中失败了,其中直接使用的assign 函数可以工作吗?

Here is an example.这是一个例子。 See where exists("subSourceMeObject") returns FALSE after the getObjectFromCalledProgram() function, but TRUE after the assign() function:看看在 getObjectFromCalledProgram() 函数之后,exists("subSourceMeObject") 返回 FALSE,但在assign() 函数之后返回 TRUE:

## Program1 ##
getObjectFromCalledProgram <-function(objectName) assign(x=objectName, value=calledProgram[[objectName]], env = parent.frame())

# set up the program files to be source()-ed
Program3 <- tempfile()  # this program will be called (source()-ed) within a source()-ed program.
writeLines(c('  ## Program3 ## ','  Program3Object <- "object3" '), con=Program3)

Program2 <- tempfile()     # this will be the secondary program, called (source()-ed) by this main program.
writeLines(c(' ## Program2 ##                                                '
             , ' Program2Object <- "object2"                                   '
             , ' source(Program3, local=calledProgram <- new.env(), echo = T)  '
             , ' # Use getObjectFromCalledProgram() to put subSourceMeObject into Program2\'s environment.'           
             , ' (getObjectFromCalledProgram("Program3Object"))                '
             , ' # That DOES NOT WORK. It returns NULL. I.e., the assign() within getObjectFromCalledProgram() fails.'
             , ' exists("Program3Object")  # returns FALSE                     '
             , ' # Use assign() directly rather than within the Program3Object function. WORKS.'
             , ' (assign("Program3Object", calledProgram[["Program3Object"]])) '
             , ' exists("Program3Object")  # returns TRUE                      '
             , ' # That WORKS. It returns "object2". I.e., the assigs() function did pull Program3Object into Program2\'s environment. '
)
, con=Program2
)

# source() those program files.
source(Program2, local=calledProgram <- new.env(), echo = T)
# Pull the created Program2Object and Program3Object into Program1's unlist(lapply(c("Program2Object", "Program3Object"), exists))  # the objects don't exist in Program1's environment.
environment, to show that getObjectFromCalledProgram() works.
(getObjectFromCalledProgram("Program2Object"))  # put Program2Object into Program1's environment.
(getObjectFromCalledProgram("Program3Object"))  # put Program3Object into Program1's environment.
unlist(lapply(c("Program2Object", "Program3Object"), exists))  # the objects now exist in Program1's environment.
rm(getObjectFromCalledProgram, Program2, Program3, Program2Object, Program3Object)  # Clean up.

I figured it out, thanks to https://stackoverflow.com/a/49385368/3799203 .我想通了,感谢https://stackoverflow.com/a/49385368/3799203 Rather than myArg = rObject , I needed to use myArg = parent.frame()$rObject , to tell R to look in the calling environment for rObject, rather than looking in the enclosing environment (ie, rather than looking in the environment where the function was created).而不是myArg = rObject ,我需要使用myArg = parent.frame()$rObject ,告诉 R 在调用环境中寻找 rObject,而不是在封闭环境中寻找(即,而不是在环境中寻找函数已创建)。

Details: From http://adv-r.had.co.nz/Environments.html#function-envs I learned that in R, function default values are taken from the function's enclosing environment, not the calling environment (the environment in which the function was called).详细信息:从http://adv-r.had.co.nz/Environments.html#function-envs我了解到,在 R 中,函数默认值取自函数的封闭环境,而不是调用环境(其中函数被调用)。 I'm guessing that "look in the containing environment" approach is handy when creating packages, since it lets functions find other things in the same package.我猜在创建包时“查看包含环境”方法很方便,因为它让函数可以在同一个包中找到其他东西。

# Simple example: x in myArg=x uses the x object in the enclosing environment, i.e., the environment where myF was created.
myF <- function(myArg=x) {print(paste("myF returns:",myArg))}
x <- "x from enclosing environment"
myF()
#> [1] "myF returns: x from enclosing environment"
with(env2<-new.env(), {
  x <- "x from calling environment"
  myF()  # returns the value of x from in the environment where the function was created.
  myF(myArg=x)  # If I explicitly pass a value to the myArg argument, R starts its search for x from the current environment.
})
#> [1] "myF returns: x from enclosing environment"
#> [1] "myF returns: x from calling environment"
rm(x, myF, env2)

# Use parent.frame()$... to have the default argument values set by the calling environment rather than by the containing environment:
myF <- function(myArg=parent.frame()$x) {print(paste("myF returns:",myArg))}
x <- "x from enclosing environment"
myF()
#> [1] "myF returns: x from enclosing environment"
with(env2<-new.env(), {
  x <- "x from calling environment"
  myF()  # the function used "parent.frame()$x" as the default value, R starts its search for x from the calling environment (the environment where the function was executed).
  myF(myArg=x)
})
#> [1] "myF returns: x from calling environment"
#> [1] "myF returns: x from calling environment"
rm(x, myF, env2)

Created on 2021-10-15 by the reprex package (v2.0.1)reprex 包(v2.0.1) 于 2021 年 10 月 15 日创建

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

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