G. B.

个人博客

且将新火试新茶,诗酒趁年华


环境

环境

小知识点

  1. 函数的环境分为四种:
    • 封闭环境:封闭环境就是创建函数的环境,每个函数有且只有一个封闭环境
    • 执行环境:调用函数创建了一个临时的执行环境,在函数执行结束后它被立即销毁
    • 调用环境:每个执行环境都与一个调用环境相关联,它说明函数在哪里被调用
    • 绑定环境:使用<-将函数与一个名字绑定在一起,这样就定义了一个绑定环境
  2. 每个环境都有父环境,父环境的存在是为了实现词法作用域(lexical scoping),即如果一个对象在当前环境中找不到,R就会在它的所有父环境中去寻找,直至找到为止;只有空环境没有父环境

  3. 环境之间只有向上的父关系,它们之间没有反向链接,即给定一个环境我们无法找到它的子环境。

  4. parent.frame()函数给出括号内函数的调用环境。

  5. 四个特殊环境:
    • globalenv(),全局环境,它的父环境是最后一个加载的包
    • baseenv(),基础环境,R基础软件包的环境,它的父环境是空环境
    • emptyenv(),空环境
    • environment(),当前环境
  6. search()列出全局环境的所有父环境,这称为搜索路径。

  7. as.environment()用来访问搜索列表中的任何环境。

  8. 环境的搜索路径可以表示为:

    globalenv() --> 加载包 --> baseenv() --> emptyenv()
    
  9. parent.env()用来查看某个环境的父环境。

  10. ls()默认不显示以”.”开头的名字,可以通过设置all.names = TRUE来显示。

  11. $[[]]只在一个环境中查找,如果不存在与该名字绑定的值就返回NULL;但get()使用普通的作用域法则,会在当前环境及所有父环境中查找。

  12. 在环境中删除对象不能像在列表中删除对象一样将其设置为NULL,因为这样会创建一个对NULL的新绑定,使用rm(),例:

    e <- new.env()
    e$a <- 1
    e$a <- NULL
    ls(e)
    #> [1] "a"
    rm("a", envir = e)
    ls(e)
    #> character(0)
    
  13. 可以使用exists()来确定一个绑定关系是否存在,与get()一样,它们都是遵循词法作用域原则的,如果不希望在父环境中查找,可以设置inherits = FALSE

  14. 使用identical()来对两个环境进行比较。

  15. 给定一个名字,pryr::where()会使用词法作用域原则找到定义这个名字的环境,默认从函数的调用环境开始,因为函数在执行时会产生执行环境,每个执行环境有两个父环境,一个调用环境与一个封闭环境,R的词法作用域法则只使用封闭父环境,如果不设置从调用环境开始,调用环境中的所有对象都查找不到。

  16. 使用循环比使用函数递归要快,因为减少了函数的调用次数

  17. 每个软件包都有两个与它相关联的环境:命名空间环境和软件包环境。

    一个软件包中的函数是在命名空间中创建的,所以这个函数的封闭环境指向命名空间,同时这个函数还有两个绑定环境,分别存在于命名空间环境中和软件包环境中。

  18. 一个软件包中的函数,如果它的定义使用了其他函数,那么它在查找这个函数时首先会在命名空间环境中查找,并且永远不会在globalenv()中查找,所以我们在全局环境中自定义的同名函数不会影响到包中的函数。查找流程可以表示为:

    函数 –> 本包的命名空间环境 –> 其它加载包的命名空间环境 –> base包的命名空间环境

  19. 对于存在嵌套的函数,父函数的执行环境就是子函数的封闭环境。

  20. 非常有用的函数:
    • 延时绑定:不立即将结果赋值给对象,它创建和存储一个约定(promise,即未被求解的参数),在需要时对约定中的表达式进行求值。使用pryr::"%<d-%",它是对基础包中的delayedAssign()的包装。
    • 主动绑定:不是绑定到常量对象,而是在每次访问时都要重新计算,用来实现引用类字段;暂时没想到使用场景。使用pryr::"%<a-%",它是对基础包中的makeActiveBinding()的包装。
  21. 对于非常大的键值对数据,使用hashmap来存储提取效率非常高,因为hashmap根据名称查找对象的时间复杂度为O(1),它实际上是通过数组角标来定位数据的,每一个键值对在存入hashmap时会经过一个转化函数f(x)得到一个位置,键值对随即被存入这个位置中,在没有位置冲突的hashmap中查找的时间复杂度的确为O(1)。

    有一个名为fastmap的库实现了快速的键值对存储。