函数--如何显示环境的名称而不是其内存地址?

9

如何在函数内以类似于内置函数的方式显示环境名称?例如,当我键入基本包中可用的mean函数时,我可以看到环境为“namespace:base”。

mean

   function (x, ...) 
   UseMethod("mean")
   <bytecode: 0x0547f17c>
   **<environment: namespace:base>**

然而,当我将一个函数附加到新创建的环境上时,在函数(f)内部访问自由变量(z)的值时,它会自动驻留在.GlobalEnv环境中,环境的名称不会显示在函数内部,但是(e1)环境的内存地址"0x051abd60"可以看到。

     e1 <- new.env()
     e1$z <- 10
     f <- function(x) {
           x + z 
      }
     environment(f) = e1
     f

               function(x) {
                    x + z 
               }
               **<environment: 0x051abd60>**

为什么我会看到这种行为?为什么我在函数内部无法像R内置函数和来自各种R包的函数那样获取我的环境名称?环境数据结构和由search()提供的.GlobalEnv环境之间有什么区别吗?
非常感谢任何关于此行为背后动机的指针。
谢谢。
5个回答

11
您可以使用attr来设置环境名称,示例如下:
e <- new.env()
attr(e, "name") <- "xyzzy"

environmentName(e)
## [1] "xyzzy"

这不是我一开始想要的,但却正是我所需要的。 - Cyan

5

如果我没记错的话,R语言中包和命名空间的环境名称是在C语言级别分配的。因此,用户创建的环境不会显示名称。虽然有一个名为environmentName()的基础函数,但你不能在R中设置环境名称,它只会返回在C级别分配的名字。它真正的意义只是适用于包和命名空间,而不是其他环境。


我尝试使用 environmentName(e1) <- "e1" 将名称添加到环境中,但出现了错误,如“找不到函数 environmentName...”。此外,每个环境都有其独特的配置文件。我还没有使用过包,但了解应用于 R 变量及其数据结构的命名空间行为非常有趣。 - Sathish
你猜测过 environmentName() 函数的用途吗? - Sathish
这大部分是错误的,详情请查看我的回答。 - moodymudskipper

4

大多数环境没有名称 - 环境的名称是环境的特殊属性,而不是指向该环境的对象的名称。例如,在以下情况下,您期望f的环境的“名称”是什么?

e1 <- new.env()
e1$z <- 10
e2 <- e1
e3 <- e1

f <- function(x) {
  x + z 
}
environment(f) <- e1

identical(e1, e2)
identical(e1, e3)

f的环境没有名称,但是它被赋予了指向e1、e2或e3的指针。 - Sathish
我的理解是,有些环境具有“特殊名称属性”。例如:内置环境带有名称,但用户定义的环境不允许添加名称特殊属性。虽然我没有看到在用户定义的环境中具有此特殊属性的特定用途,因为环境与其对象名称相关联,并且在内部分配了指针以在环境和其分配的对象之间进行交互。 - Sathish

3
您可以使用我最近发布的包envnames作为解决此问题的方法。

在您的示例中,如果您使用该包的environment_name()函数来检索函数f()的环境,则会得到"e1",而不是使用内置函数environmentName()得到的""

library(envnames)
e1 <- new.env()
e1$z <- 10
f <- function(x) {
   x + z 
}
environment(f) = e1
environment_name(environment(f))

输出结果为:

[1] "e1"

在 Hadley 给出的示例中,当许多环境指向同一个环境时,您将在命名数组中获得所有这些环境名称。
library(envnames)
e1 <- new.env()
e1$z <- 10
e2 <- e1
e3 <- e1

f <- function(x) {
  x + z 
}
environment(f) <- e1
environment_name(environment(f))

输出包括每个环境的位置,作为返回数组的名称属性:

R_GlobalEnv R_GlobalEnv R_GlobalEnv 
       "e1"        "e2"        "e3"

最后,由于您提到查看环境的内存地址并不能告诉我们关于所讨论的用户定义环境的太多信息,因此您可以将内存地址作为输入参数传递给environment_name()函数,以获取与内存地址相关联的环境(或环境)的名称。

下面的代码段和输出说明了这一点(其中的代码在一个单一环境的您的示例上运行):

> f
function(x) {
       x + z 
    }
<environment: 0x0000000013f15870>
> environment_name("<environment: 0x0000000013f15870>")
[1] "e1"

1

根据我的实验(未阅读C代码),environmentName()有两个作用:

  • 首先检查您的环境是否具有.__NAMESPACE__.$spec["name"],其中.__NAMESPACE__.是一个环境,spec是一个命名字符向量(顺便说一下,这会使您的环境成为命名空间)。
  • 如果没有找到,则回退到attr(, "name")

然而,设置attr(, "name")不会影响函数的打印方式。

# initiate
e <- new.env()
fun <- function() {}
environment(fun) <- e
e
#> <environment: 0x14b28d3a0>
fun
#> function() {}
#> <environment: 0x14b28d3a0>

# set attr(, "name") : environmentName() is changed, function still prints the same
attr(e, "name") <- "NAME1"
environmentName(e)
#> [1] "NAME1"
e
#> <environment: 0x14b28d3a0>
#> attr(,"name")
#> [1] "NAME1"
fun
#> function() {}
#> <environment: 0x14b28d3a0>

# Make it a "minimal" namespace with a name, new name takes precedence, function prints differently
e[[".__NAMESPACE__."]] <- as.environment(list(spec = c(name = "NAME2")))
isNamespace(e) 
#> [1] TRUE
environmentName(e)
#> [1] "NAME2"
e
#> <environment: namespace:NAME2>
#> attr(,"name")
#> [1] "NAME1"
fun
#> function() {}
#> <environment: namespace:NAME2>

2023-03-29 使用 reprex v2.0.2 创建

一旦拥有命名空间,您可以使用setNamespaceInfo()更改其名称。

setNamespaceInfo("stats", "spec", c(name = "FOO"))
asNamespace("stats")
#> <environment: namespace:FOO>
``

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接