【r<-高级|理论】R的符号和环境

R中的每一个符号(symbol)都是定义在一个具体的环境中的。环境(environment)就是一个R对象(R中一切皆对象),其中包含给定上下文中的符号集合、与这些符号相关的对象,以及一个指向父环境的指针。

上面的定义非常的清晰,我们可以简单地把环境看做一个相对独立的空间,这个空间里包含很多物品(符号相关的对象),以及对它们的描述与表征(符号)。环境与其父环境之间通过指针相连,以沟通与外界联系。

当R解析一个符号时,会先查找当前的环境。如果在本地环境中没有匹配的符号,R就会逐级而上查找父环境中是否有能匹配的符号。

符号

符号这个概念也许刚接触R的话不会常听到,但你却实实在在每每刻刻都在用它。我记得学C语言时,对于变量赋值及其实现通常老师会举一个酒店或者旅馆的例子。如果说存储单元是一个个独立的房间,里面放了数据,那么符号就是门牌号了,指向了数据放在哪个房间。虽然R好像在概念上没有涉及寻址,但其内部必然封装了这一个过程。简单地广义地理解,R中的符号就是其他语言(C、Python等)变量、常量的泛化概念。

最简单的例子:

x <- 1

x就是符号。想必看到这里大家应该都能明白了。

我们可以推迟表达式的求值,使符号不会立即被解析。

> x <- 1
> y <- 2
> z <- 3
> v <- quote(c(x,y,z))
> eval(v)
[1] 1 2 3
> v
c(x, y, z)
> 
> x <- 5
> eval(v)
[1] 5 2 3

环境

环境也是对象,在内部R将符号映射到哈希表中。

下面展示操作环境对象的R函数。

函数 描述
assign 在envir环境中将名称x赋给value对象
get 在envir环境中获得与名称x关联的对象
exists 判断在环境envir中是否定义了名称x
objects 以向量的形式返回envir环境中定义的所有名称
remove 从envir环境中移除罗列的对象
search 以向量 形式返回所附着的包的名称
searchpaths 以向量 形式返回所附着的包的路径
attach 将数据对象添加到当前搜索路径
detach 将数据对象从当前搜索路径中移除
emptyenv 返回空环境对象。所有的环境链最终都会回到这个对象
parent.env 返回env环境的父环境
baseenv base包的环境
globalenv或.GlobalEnv 返回用户工作空间的环境(也称为全局环境)
environment 为fun函数返回环境。没有参数时返回的就是当前环境
new.env 返回一个新的环境对象

简单用一下其中的几个函数:

> x <- 1
> y <- 2
> z <- 3
> objects()
[1] "x" "y" "z"
> rm(x)
> objects()
[1] "y" "z"

函数的父环境就是创建该函数的环境。调用环境是使用该函数的环境。如果函数是在运行环境中创建的,那么父环境和调用环境是相同的。

调用堆栈

尽管函数的父环境并不一定是调用函数的环境,但函数总是可以访问到调用它的环境。与其他语言类似,R会维护一个调用环境栈。(堆栈的概念不懂的朋友可以自己百度查阅下资料)

下面列出了操作调用栈的函数。

函数 描述
sys.call 返回一个包含当前函数调用的语言对象
sys.frame 返回调用环境
sys.nframe 返回当前帧的编号(在堆栈中位置)
sys.function 返回当前计算的函数
sys.parent 返回父帧的编号
sys.calls 返回栈中所有帧的调用
sys.frames 返回栈中所有的环境
sys.parents 返回栈中每个帧的父帧
sys.on.exit 返回当前帧on.exit所使用的表达式
sys.status 返回一个列表,其中包括调用sys.calls,sys.parents和sys.frames的结果
parent.frame 返回sys.frame(sys.parent(n))。换言之,返回父帧

异常

当输入了不正确的表达式时,R会给出错误的提示。例如

> 12 / 'hat'
Error in 12/"hat" : 二进列运算符中有非数值参数

有的时候,R会发出警告:

> if (c(TRUE, FALSE)) TRUE else FALSE
[1] TRUE
Warning message:
In if (c(TRUE, FALSE)) TRUE else FALSE :
  条件的长度大于一,因此只能用其第一元素

和其他现代编程语言一样,R也可以在发生异常事件时发出异常信号,也可以捕获异常信号。异常处理和环境是密切相关的,在异常发生时,R解释器需要中止当前的函数,并向调用环境发出异常信号。

提示错误

用法非常简单,下面给出发错误和警告的小例子,我们在编写程序的时候照着用就可以了。

报错

> doWork <- function(filename){
+   if(file.exists(filename)){
+     read.delim(filename)
+     }else{
+       stop("Could not open the file: ", filename)
+   }
+ }
> doWork("file that doesn't exist")
Error in doWork("file that doesn't exist") : 
  Could not open the file: file that doesn't exist

报警告

> doWork <- function(filename){
+   if(file.exists(filename)){
+     "la la la"
+     }else{
+     warning("File does not exists: ", filename)
+ }
+ }
> doWork("another file that doesn't exist")
Warning message:
In doWork("another file that doesn't exist") :
  File does not exists: another file that doesn't exist

如果只是想要告知用户某些信息,可以使用message函数:

> doNothing <- function(x) {
+   message("This function does nothing.")
+ }
> doNothing("another input value")
This function does nothing.

捕获错误

try函数可以简单地实现这个目的。这个函数隐藏了R异常处理的复杂之处。下面用一个例子加以说明:

> res <- try({x <- 1}, silent=TRUE)
> res
[1] 1
> res <- try({open("file that doesn't exist")}, silent=TRUE)
> res
[1] "Error in UseMethod(\"open\") : \"open\"没有适用于\"character\"目标对象的方法\n"
attr(,"class")
[1] "try-error"
attr(,"condition")
<simpleError in UseMethod("open"): "open"没有适用于"character"目标对象的方法>

try带两个参数,exprsilent。第一个表示要尝试的R表达式。第二个参数表示是否要把错误信息显示到R控制台(或者标准错误输出stderr)。如果表达式运行发生了错误,try会返回一个try-error类对象。

另一个更为强大的函数是tryCatchtryCatch有三组参数:要尝试的表达式;一组用于不同条件的处理器;以及一个最终要计算的表达式。

格式:

tryCatch(expression, handler1, handler2, ..., finally=finalexpr)

R解释器首先会对expression求值。如果发生了某种情况,R就会选择相应的处理器。在表达式计算完后,就会计算finalexpr。(如果计算了这个表达式,意味着任何处理器都不会被激活)


学习整理自《R核心技术手册》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,622评论 18 399
  • 一、温故而知新 1. 内存不够怎么办 内存简单分配策略的问题地址空间不隔离内存使用效率低程序运行的地址不确定 关于...
    SeanCST阅读 7,802评论 0 27
  • 前言 人生苦多,快来 Kotlin ,快速学习Kotlin! 什么是Kotlin? Kotlin 是种静态类型编程...
    任半生嚣狂阅读 26,201评论 9 118
  • 设计师逻辑:中文版:启动动画 -> 主页面国际版:启动图片 -> 主页面 转换为程序逻辑:zh-Hans:Laun...
    双湾阅读 3,142评论 0 2
  • 不知什么时候,我们每天都捧着手机不放,大量的人际关系、心理以及物质的需求统统转移到股掌之间。有时我在质疑,我们这个...
    三楼浮华阅读 310评论 0 0