6.2 环境

我们所创建的所有变量都需要存储在某处,即环境。环境本身也是另一种类型的变量,我们可以像对待其他变量一样地分配和操作它们,并将其以参数的形式传递到函数中。它们与列表密切相关,也能用于存储不同类型的变量。事实上,大部分用于列表的语法也同样适用于环境,而且我们可以强制地把列表当作环境使用(反之亦然)。

通常情况下,你不需要直接与环境打交道。例如,你在命令提示符下分配一个变量,它会自动进入全局环境(也称为用户工作区)中。当你调用函数时,将会自动创建一个环境用于存储与此函数相关的变量。不过,当你要了解变量的作用域或调试代码以检查调用栈时,掌握环境的基础知识将有所帮助。

有些烦人的是,环境的创建不是使用environment函数(该函数将返回包含特定函数的环境),而用new.env函数:

  1. an_environment<-new.env()

向环境中分配变量的方式与列表完全相同。可以使用双方括号或美元符号运算符。和列表一样,环境变量的类型和大小可以不同:

  1. an_environment[["pythag"]]<-c(12, 15, 20, 21) # 请查看http://oeis.org/A156683
  2. an_environment$root<-polyroot(c(6, -5, 1))

2.3节中见到assign函数还有一个可选的环境参数,用于指定变量的存储位置:

  1. assign(
  2. "moonday",
  3. weekdays(as.Date("1969/07/20")),
  4. an_environment
  5. )

检索变量的方式也是如此:你可以使用列表的索引语法,或assign的对立函数get

  1. an_environment[["pythag"]]
  2. ## [1] 12 15 20 21
  3. an_environment$root
  4. ## [1] 2+0i 3-0i
  5. get("moonday", an_environment)
  6. ## [1] "Sunday"

可把环境参数传入lsls.str函数中,列出它的所有内容:

  1. ls(envir = an_environment)
  2. ## [1] "moonday" "pythag" "root"
  3. ls.str(envir = an_environment)
  4. ## moonday: chr "Sunday"
  5. ## pythag: num [1:4] 12 15 20 21
  6. ## root: cplx [1:2] 2+0i 3-0i

可用exists函数测试变量是否在环境中:

  1. exists("pythag", an_environment)
  2. ## [1] TRUE

很明显,使用as.listas. environment函数能分别实现从环境到列表或相反过程的转换。在后一种情况中,还可以使用list2env函数,它在创建环境时更为灵活:

  1. # 转换为列表
  2. (a_list<-as.list(an_environment))
  3. ## $pythag
  4. ## [1] 12 15 20 21
  5. ##
  6. ## $moonday
  7. ## [1] "Sunday"
  8. ##
  9. ## $root
  10. ## [1] 2+0i 3-0i
  11. #...再转换回来。以下两行代码的效果一样。
  12. as.environment(a_list)
  13. ## <environment: 0x000000004a6fe290>
  14. list2env(a_list)
  15. ## <environment: 0x000000004ad10288>

所有的环境都是嵌套的,这意味着它们必须有一个父环境(位于顶端的特殊环境空环境除外)。默认情况下,existsget函数也将在父环境中寻找变量。可通过给它们传入inherits = FALSE来改变这种行为,使它们仅在指定的环境中搜索:

  1. nested_environment<-new.env(parent=an_environment)
  2. exists("pythag", nested_environment)
  3. ## [1] TRUE
  4. exists("pythag", nested_environment, inherits=FALSE)
  5. ## [1] FALSE

注意

“框”(frame)这个词几乎可以与“环境”互换(要想了解此术语,请参考随R附带的语言定义手册2.1.10节)。这意味着工作环境中的一些函数名称里可能有“frame”,最常见的是parent.frame

下列快捷函数可以同时访问全局环境(那些你从命令提示符中分配的变量的存储空间)和基础环境(那些R基础包中自带的基础函数和变量):

  1. non_stormers<<-c(3, 7, 8, 13, 17, 18, 21) # 参见 http://oeis.org/A002312
  2. get("non_stormers", envir=globalenv())
  3. ## [1] 3 7 8 13 17 18 21
  4. head(ls(envir=baseenv()), 20)
  5. ## [1] "-" "-.Date" "-.POSIXt"
  6. ## [4] "!" "!.hexmode" "!.octmode"
  7. ## [7] "!=" "$" "$.data.frame"
  8. ## [10] "$.DLLInfo" "$.package_version" "$<-"
  9. ## [13] "$<-.data.frame" "%%" "%*%"
  10. ## [16] "%/%" "%in%" "%o%"
  11. ## [19] "%x%" "&"

在另外两种情况下也可能遇到环境。首先,当调用函数时,函数所定义的所有变量都被存储在属于该函数的环境中(函数及其环境有时称为闭包)。其次,当加载包时,包中的函数将存储于其搜索路径的环境中。这将在第10章中讨论。