「R」R的符号和环境

2020-07-06 17:41:53 浏览数 (1)

这是一篇很有实力的干货,很多技术知识连我自己现在都还未掌握。

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

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

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

符号

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

最简单的例子:

代码语言:javascript复制
x <- 1

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

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

代码语言:javascript复制
> 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

返回一个新的环境对象

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

代码语言:javascript复制
> 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会给出错误的提示。例如

代码语言:javascript复制
> 12 / 'hat'
Error in 12/"hat" : 二进列运算符中有非数值参数

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

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

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

提示错误

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

报错

代码语言:javascript复制
> 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

报警告

代码语言:javascript复制
> 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函数:

代码语言:javascript复制
> doNothing <- function(x) {
    message("This function does nothing.")
  }
> doNothing("another input value")
This function does nothing.
捕获错误

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

代码语言:javascript复制
> 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有三组参数:要尝试的表达式;一组用于不同条件的处理器;以及一个最终要计算的表达式。

格式:

代码语言:javascript复制
tryCatch(expression, handler1, handler2, ..., finally=finalexpr)

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

最后,推荐两篇很棒的相关文章:

  • 揭开R语言中环境空间的神秘面纱
  • 解密R语言函数的环境空间

0 人点赞