我对函数的不同环境有几个问题。请看以下示例:
environment(sd)
# <environment: namespace:stats>
namespace:stats是否指向函数sd的封闭环境?
pryr::where(sd)
# <environment: package:stats>
package:stats是否指向函数sd的绑定环境?
根据Advanced R by Hadley Wickham:“封闭的环境属于该功能,永远不会改变......”
但功能的封闭环境可以改变如下:
new.env <- new.env()
environment(f) <- new.env
函数'environment属性表示函数的执行环境,对吗? An online article regarding R finding stuff through environments
总结我的问题:
stats
包的两种不同环境是什么?它类似于上一篇文章in here。
答案 0 :(得分:15)
TLDR:
功能环境
在谈论某项功能时,您必须区分4种不同的环境:
find()
为您提供绑定环境。environment()
为您提供封闭的环境。为什么这很重要
每个环境都有一个特定的功能:
emptyenv()
。您可以更改封闭环境
确实,您可以更改封闭环境。它是一个函数的封闭环境,来自包,你无法改变。在这种情况下,您无法更改封闭环境,实际上是在新环境中创建副本:
> ls()
character(0)
> environment(sd)
<environment: namespace:stats>
> environment(sd) <- globalenv()
> environment(sd)
<environment: R_GlobalEnv>
> ls()
[1] "sd"
> find("sd")
[1] ".GlobalEnv" "package:stats" # two functions sd now
> rm(sd)
> environment(sd)
<environment: namespace:stats>
在这种情况下,第二个sd
将全局环境作为封闭和绑定环境,但原始的sd
仍然在包环境中找到,其封闭环境仍然是命名空间那个包
执行以下操作时可能会出现混淆:
> f <- sd
> environment(f)
<environment: namespace:stats>
> find("f")
[1] ".GlobalEnv"
这里发生了什么?封闭的环境仍然是命名空间&#39;&#39; stats&#39;&#39;。那就是创建函数的地方。但是,绑定环境现在是全球环境。那个名字&#34; f&#34;与对象绑定。
我们可以将封闭环境更改为新环境e
。如果您现在检查,则封闭环境变为e
,但e
本身为空。 f
仍然受到全球环境的约束。
> e <- new.env()
> e
<environment: 0x000000001852e0a8>
> environment(f) <- e
> find("f")
[1] ".GlobalEnv"
> environment(f)
<environment: 0x000000001852e0a8>
> ls(e)
character(0)
e
的封闭环境是全球环境。所以f
仍然可以像它的外壳是全球环境一样工作。环境e
包含在其中,因此如果在e
中找不到某些内容,则该函数将在全局环境中查找,依此类推。
但是因为e
是一个环境,所以R称之为父环境。
> parent.env(e)
<environment: R_GlobalEnv>
> f(1:3)
[1] 1
命名空间和程序包环境
这个原则也是&#34;技巧&#34;包使用:
原因很简单:只能在您所处的环境或其封闭环境中找到对象。
插图:
现在假设您创建一个环境为空的环境作为父项。如果将此作为函数的封闭环境使用,则不再起作用。因为现在你绕过了所有的包环境,所以你不能再找到一个函数了。
> orphan <- new.env(parent = emptyenv())
> environment(f) <- orphan
> f(1:3)
Error in sqrt(var(if (is.vector(x) || is.factor(x)) x else as.double(x), :
could not find function "sqrt"
父框架
这是有趣的地方。父框架或调用环境是查找作为参数传递的值的环境。但是父框架可以是另一个功能的本地环境。在这种情况下,R首先在该其他函数的本地环境中查找,然后在调用函数的封闭环境中,以及一直到全局环境,附加包的环境直到它到达空的环境。没有找到&#34;对象的地方&#34;虫子睡觉。
答案 1 :(得分:3)
environment(function)
给出函数的封闭环境(即闭包),该环境被赋予指向定义函数的环境的指针。这种约定称为词法范围,它允许您使用工厂函数之类的模式。这是一个简单的例子
factory <- function(){
# get a reference to the current environment -- i.e. the environment
# that was created when the function `factory` was called.
envir = environment()
data <- 0
add <- function(x=1){
# we can use the lexical scoping assignment operator to re-assign the value of data
data <<- data + x
# return the value of the lexically scoped variable `data`
return(data)
}
return(list(envir=envir,add=add))
}
L = factory()
# check that the environment for L$add is the environment in which it was created
identical(L$envir,environment(L$add))
#> TRUE
L$add()
#> 1
L$add(3)
#> 4
请注意,我们可以使用data
在封闭环境中重新分配assign()
的值,如下所示:
assign("data",100,L$envir)
L$add()
#> 101
此外,当我们再次调用函数factory()
时,会创建另一个新环境
并被指定为在其中定义的函数的闭包
函数调用,这是我们必须分离foo $ add()函数的原因
它们适用于各自独立的环境:
M = factory()
M$add()
#> 1
#> 2
L$add()
#> 102
上面的工厂函数通过继续搜索变量(以及使用作用域赋值运算符)来说明函数与其封闭环境之间的链接,而下面通过Promises说明了本地环境与调用帧之间的链接这是R在函数调用中传递变量的方式。
具体来说,当你调用一个函数时,R会为传递的变量和表达式的值创建promises。当参数为force()
'或使用时,通过在调用环境的上下文中评估Promise,从变量/表达式传递(复制)Promise的这些值 - 而不是更快!
例如,此工厂函数接受一个参数,该参数存储为promise,直到调用返回的函数:
factory2 <- function(x){
out <-function(){
return(x)
}
return(out)
}
现在factory2
在某些情况下表现得很直观:
y = 1
f = factory2(y)
f()
#> 1
但不在其他人中:
y = 1
h = factory2(y)
y = 2
h()
#> 2
因为在调用y
之前不会评估表达式h()
的承诺,而在第二个示例中,y
的值为2!当然,现在已经通过Promise评估将值从调用环境复制到本地环境中,更改y的值不会影响h()
返回的值:
y = 3
h()
#> 2