R 环境是 R 运行的基本载体,类似于 C++ 的命名空间和 MATLAB 的工作区。
环境概述
R 储存对象使用了与计算机文件管理系统类似的层级系统结构,每个对象都会被存储于环境中。环境类似于文件夹,绝大多数环境都与另一个高一层级的环境相连接(形式上类似于子文件夹和父文件夹的关系),后者被称为前者的父环境。
可以使用 parenvs
函数(于 pryr
包中)查看 R 的环境系统。
与文件管理器相同,R 处于工作状态的环境也只有一个,一般可以称为活动环境或当前环境。通常情况下,活动环境就是全局环境,但是在运行函数(调用堆栈)时,活动环境会相应地发生改变。environment
函数可以用来查看当前的活动环境:
1 | > environment() |
在 R 中,命令行中运行的所有命令均在全局环境中运行,因此其产生的所有对象也都会被储存在全局环境中。这一环境类似于 MATLAB 中的用户工作区。
环境操作
可以使用 R 提供的辅助函数来对 R 环境进行操作。
as.environment
函数可以指向环境树中的任意一个环境。其接受一个环境名称(字符串)作为输入,并返回该名称所对应的环境。
在众多的环境树中,有三个环境拥有自己的调用函数:全局环境(R_GlobalEnv
)、基环境(base
)和空环境(R_EmptyEnv
)。它们对应的调用函数分别为:globalenv()
、baseenv()
和 emptyenv()
。
parent.env
函数可以用来查看某个特定环境的父环境,例如 parent.env(globalenv())
。
空环境是 R 中唯一没有父环境的环境,相当于文件系统的根目录
ls
和 ls.str
函数可以用来查看储存在对应环境中的对象。ls
只返回对象名称,ls.str
则会大致展示每个对象的结构。该函数接受环境的调用函数。
可以使用 环境调用函数$对象名
来提取对象,例如:
1 | > x <- 1:10 |
assign
函数可以将对象存储于某个特定环境中。其格式为:assign(x, value, envir)
,x
为新对象的名称(以字符串格式,即双引号包裹的形式给出),value
为新对象的取值,envir
为对象的储存环境,例如:
1 | > assign("x", 1:12, envir = globalenv()) |
assign 函数会覆盖在同环境下同对象的值
作用域
和其它编程语言一样,R 也存在着限制代码名称作用范围的规则,这些规则被称为 R 的作用于规则:
- R 首先会在活动环境中搜索对象;
- 在命令行中,活动环境就是全局环境;
- 未搜索到对象时,R 会搜索当前环境的父环境,以此类推,直至空环境。
总的来说,就是由当前环境开始搜索,沿父环境方向追索,直至达到空环境
从概念上说,R 函数也是 R 对象,也遵循上述的作用于规则
赋值
给活动环境中的对象赋值时,R 会将该值以此对象的名称存储在活动环境中。在已存在同名对象的情况下,R 会覆盖掉原对象。
函数求值
由于 R 函数在运行时会创建新的活动环境,因此原环境会成为函数运行时的父环境。也是出于这个原因,函数中定义的对象不会影响到原环境中的同名对象。因此,每次函数求值都会创建一个运行时环境。
函数每次产生的运行时环境从概念上讲一定不相同,但在名称上可能相同
使用 environment
函数可以查看函数的定义环境,即函数被创建时所在的环境,也被称作原环境。而函数在被调用前的环境被称为调用环境。
函数的原环境与函数的调用环境不一定是同一环境。例如在环境 Env
中定义的函数在命令行中运行时,原环境为 Env
,调用环境为 R_GlobalEnv
;而在命令行中定义的函数在命令行中运行时,原环境和调用环境均为 R_GlobalEnv
闭包
由于全局环境经常发生对象改动,因此直接使用函数可能会导致对象变动,甚至消失。因此为避免此状况,可以将函数放置在其他函数中运行。该处理方式在 R 中被称为闭包。常见做法是在全局环境中定义一个函数,然后再在该函数中定义其他函数或对象,并在最后一行将这些函数或对象组成列表(或其它结构)赋值给原环境为全局环境的这个函数。
依据该原理,可以构建嵌套闭包