R语言-环境系统

一、环境介绍
我们知道计算机中的文件总是储存在一个个文件夹之中,而这些文件夹又可能储存在另一个父文件夹之中。R存储对象也存在这样一个类似的层级系统结构。每个对象都存储在一个环境(environment)中,环境是一个类似于列表的对象,概念上接近文件夹。
我们可以通过加载pryr包中的parenvs函数查看R的环境系统,parevns(all=TRUE)会返回当前会话包含的环境列表,这取决于你加载了那些R包:

> install.packages("pryr")
> library(pryr)
> parenvs(all=TRUE)
   label                             name               
1  <environment: R_GlobalEnv>        ""                 
2  <environment: package:pryr>       "package:pryr"     
3  <environment: 0x00000000094d9ce0> "tools:rstudio"    
4  <environment: package:stats>      "package:stats"    
5  <environment: package:graphics>   "package:graphics" 
6  <environment: package:grDevices>  "package:grDevices"
7  <environment: package:utils>      "package:utils"    
8  <environment: package:datasets>   "package:datasets" 
9  <environment: package:methods>    "package:methods"  
10 <environment: 0x0000000008224e38> "Autoloads"        
11 <environment: base>               ""                 
12 <environment: R_EmptyEnv>         ""  

结果中越前面的环境等级越低,越后面的环境层级越高。其中R_EmptyEnv是唯一没有父环境的环境。将R的环境系统比作文件夹只是一种类比,R的环境系统实质上是存储在RAM内存中,并且R环境之间的关系并不是嵌套关系。而是更接近树形结构,每一个环境都与一个父环境相连,但这样的连结是单向的:我们可以很轻松的找到某个环境的父环境,却难以直接找到一个环境的子环境。因此R环境的树形结构不支持自上而下的搜索


二、操作R环境

1.获取/调用环境

as.environment(“……”)可以返回字符串名称对应的环境,而全局环境(R_GlobalEnv)、基环境(base)和空环境(R_EmptyEnv)拥有自己的调用函数,具体如下:

> as.environment("package:pryr")
<environment: package:pryr>
attr(,"name")
[1] "package:pryr"
attr(,"path")
[1] "D:/R-3.4.1/library/pryr"

> globalenv()
<environment: R_GlobalEnv>
> baseenv()
<environment: base>
> emptyenv()
<environment: R_EmptyEnv>

2.查看父环境

使用parent.env()函数可以产看某一环境的父环境,当然空环境是不存在父环境的:

> parent.env(globalenv())
<environment: package:pryr>
attr(,"name")
[1] "package:pryr"
attr(,"path")
[1] "D:/R-3.4.1/library/pryr"
> parent.env(emptyenv())
Error in parent.env(emptyenv()) : the empty environment has no parent

3.查看环境中的对象

使用ls或ls.str命令可以查看存储在环境中的对象,ls命令只会返回对象名称;ls.str命令则会大致展示每个对象的结构。

> ls(emptyenv())
character(0)
> ls(globalenv())
 [1] "C1"      "C2"      "C3"      "cardpos" "cards"  
 [6] "data"    "int"     "list"    "lst"     "matrix" 
[11] "matrix2" "now"     "num"     "roll"    "roll2"  
[16] "roll3"   "test"    "test1"   "x"      
> ls.str(globalenv())
C1 :  chr [1:6] "A" "B" "C" "A" "B" "C"
C2 :  chr [1:6] "红" "红" "红" "黑" "黑" "黑"
C3 :  num [1:6] 3 2 1 3 2 1
……

通过RStudio的环境面板可以查看对应环境所含的对象
这里写图片描述

4.提取和存储对象

可以使用$符号在特定环境中提取对象,如

> baseenv()$T
[1] TRUE

当然你也可以使用assign()函数将对象存储到某个特定环境中,如
assign("new","Hello Global",envir = globalenv())
其中第一个参数是对象名称,第二个参数是对象取值,第三个参数是某特定环境的名称


三、作用域规则

1.活动环境

任何时候R的活动环境都只有一个,所有的新对象都会默认被存储到活动环境,搜索对象时,R也会优先搜索该环境,这个环境有人形象地将之称为活动环境(active environment)。通常来说,活动环境就是全局环境,但是当你运行函数时,活动环境可能就变化了,可以使用environment()函数查看当前活动环境:

> environment()
<environment: R_GlobalEnv>

2.作用域规则

当你调用不在全局环境中的对象时,R会遵循一系列的规则去搜索该对象,这些规则被称为作用于规则(scoping rules):

(1) R首先在当前的活动环境中搜索对象。
(2) .在命令行中工作时,活动环境就是全局环境,因此命令行中的所有调用都发生在全局环境

当所要搜索的对象不在当前的活动环境中时,R会按照以下规则进行搜索。

(3) 当R在某个环境中没有搜索到对象时,R会进入该环境的父环境,然后进入该父环境的父环境,以此类推直到在某个环境中找到该对象,或者到达环境树的顶层环境(空环境),搜索才停止。

需要注意,函数也是一种对象,R存储和搜索函数的方式与其存储和搜索其他对象的方式并无差别。


四、函数赋值

1.运行环境与原环境

当运行R函数时,若函数主体中涉及对象赋值,则必然有一些临时对来承担这些任务,那么R必然要将这些临时对象存储在活动环境中。但如果这么做就可能影响到函数所在环境的同名对象,为了避免这种情况,R在每次运行函数时,都会创建一个新的活动环境,函数的运行是在新环境中运行的,这个环境我们称之为运行环境
可以通过下面的show_env()函数,展示相关信息:

show_env<-function(){
  a<-1
  list(ran.in=environment(),parent=parent.env(environment()),objects=ls.str(environment()))
}
show_env()
$ran.in
<environment: 0x000000001dec3428>

$parent
<environment: R_GlobalEnv>

$objects
a :  num 1

show_env本身也是一个函数,因此调用show_env()时R也会创建一个新的运行环境来对该函数求值。如果你在运行一次该函数,会发现运行环境又发生了改变,但这些运行环境的父环境都是全局环境。
现在我们讨论一下,运行环境的父环境应该是什么?
R会将一个函数的运行环境与第一次创建该函数时的环境相连接,也就是该函数所有运行环境的父环境,这个环境称为“原环境(origin environment)”,可以通过environment()函数查看某个函数的原环境:

> environment(parenvs)
<environment: namespace:pryr>

现在我们从实际例子出发理解两类函数运行过程:
(1) 含参

values<-c(0,0,0)
changeV1<-function(values){
  values[1]<-1
  values
}
values<-changeV1(values)
> values
[1] 1 0 0

这里写图片描述
如上图所示,由于changeV1中传入参数values,所以会在运行环境即全局环境的一个子环境中生成一个values的对象作为values的副本,函数主体对values副本进行操作,之后在全局环境中将values副本赋给values,完成环境的一个循环。

(2) 不含参

values<-c(0,0,0)
changeV2<-function(){
  values[2]=2
}
changeV2()
> values
[1] 0 0 0

这里写图片描述
在changeV2中涉及对象values,但并没有相应参数传入,因此依照对象的搜索策略,会在父环境中搜索,此时能够搜索到values,但R并不会对其直接操作,而是会自动生成一个values副本存储在运行环境中,所以全局环境中的values并不会改变。

2.跨环境存储与闭包操作
可以发现,我们都并没有能够做到在函数内部实现对全局变量values进行修改!那么我们应该怎么做呢?
是的,上面我们介绍的assign函数就可以实现这样的功能:

values<-c(0,0,0)
changeV3<-function(){
  values[2]=2
  assign("values",values,envir = globalenv())
}
changeV3()
> values
[1] 0 2 0

仔细思考可以发现,这种跨平台修改全局环境对象的方式并不好,因为全局环境要进行各种操作,若将所有函数相关的对象都存储在全局变量显然是很麻烦的一件事情。
如果能够将每个函数所需的对象都放置在一个单独创造的运行环境却是一个好主意,这样只要第一次将所需值传入,之后就算对全局环境中的对象进行改变也不会影响函数的使用。

values<-c(0,0,0)
#定义函数setup进行闭包
setup<-function(values){
  list1<-list(ran.in=environment(),parent=parent.env(environment()),objects=ls.str(environment()))
  CHANGE<-function(){
    values[2]=2
    assign("values",values,envir = parent.env(environment())
    list(ran.in=environment(),parent=parent.env(environment()),objects=ls.str(environment()))
  }
  list(change=CHANGE,list1)
}
tests<-setup(values)
> tests  #展示setup的输出list
$change
function () 
{
    values[2] = 2
    assign("values", values, envir = parent.env(environment())
    list(ran.in = environment(), parent = parent.env(environment()), 
        objects = ls.str(environment()))
}
<environment: 0x000000001e69dea8>

[[2]]
[[2]]$ran.in
<environment: 0x000000001e69dea8>

[[2]]$parent
<environment: R_GlobalEnv>

[[2]]$objects
values :  num [1:3] 0 0 0
> tests$change()
$ran.in
<environment: 0x000000001ead4450>

$parent
<environment: 0x000000001e69dea8>

$objects
values :  num [1:3] 0 2 0

这里写图片描述
在上述代码中,新建了一个setup函数,可以把它的运行空间看做我们人为开辟出来的一块储存空间,而它在他的内部定义了一个CHANGE函数,用以修改setuo运行环境中的变量,并返回一个包含change函数的list。(需要注意:函数的返回值类似于java的print,会将结果打印在控制台,但并不会用变量存储)
当通过tests使用list中的change函数时,因为change函数的原环境就是setup的运行环境,所以change函数能够始终找到setup运行环境中的values副本对象并进行相关赋值。

这样的一系列处理方式称为“闭包”,这样的操作可以使得你更加肆无忌惮的在操作台对原始数据进行操作,只要你不把函数setup或者函数setup的返回值进行修改就好。

  • 6
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
R语言chap-1 introduction to R 中文版注释》是一本介绍R语言的书籍,旨在帮助读者了解R语言的基础知识和概念。 该书第一章主要介绍了R语言的概述和安装方法。R语言是一种用于数据分析和统计建模的开源编程语言,在科研、数据科学和商业领域广泛应用。通过它,用户可以对数据进行处理、可视化、统计分析和机器学习等操作。 该章节首先介绍了R语言的特点和优势,如开源、跨平台、扩展性强等。其次,详细说明了R语言的安装步骤,包括下载R软件包、安装R软件、配置环境等。同时,还提供了一些常见的R语言安装问题和解决方法。 该章还介绍了R语言的基本使用方法。包括了R语言的命令行界面和集成开发环境(IDE)的使用,如RStudio等。同时,还介绍了R语言的常用数据类型,如向量、列表、矩阵、数据框等,以及关于变量的命名规则和数据的存储和读取方法。 最后,该章节还介绍了R语言的帮助系统和学习资源。R语言具有丰富的帮助文档和在线资源,用户可以通过help命令、?命令以及R语言社区等方式获取帮助和提升自己的技能。 总之,《R语言chap-1 introduction to R 中文版注释》这本书可以帮助读者从零开始学习R语言,并建立起对R语言的基本理解和操作能力。无论是初学者还是有一定经验的用户,都可以从中获得实用的知识和技巧,提升自己的数据分析和统计建模能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值