R语言编程技术(4)

  R也属于一种面向对象语言,万物皆为对象,只是它毕竟最初设计不是为了面向工程,而是面向科学研究,故而它比较奇葩,与我们大多数主流语言的面向对象设计有所不同,它是以一种名为S3系统来作面向对象架构设计,几个基础的R包都是基于S3系统设计,很多第三方包也基于S3对象系统。(S4,RC, R6这些更高级的系统这里不做讨论了)

  • S3面向对象系统
      S3面向对象系统非常简洁有效,它本质就是一种函数的泛型编程。
    eg:
print
# function (x, ...) 
# UseMethod("print")
# <bytecode: 0xbd82ca0>
# <environment: namespace:base>

  这里以最常用的函数print为例子,在控制台中打印函数原码,原码并没有显示,显示出的是一个泛型函数,泛型函数可以理解为某相同功能函数的泛化汇总,比如做飞机,做火车,做汽车,本质都是交通活动,这种泛化的设计的好处就在于需要调用函数时不需要知道具体的函数名,只需要明确要做什么操作即可。

methods(print)
#  [1] print.aareg*                                      
#  [2] print.acf*                                        
#  [3] print.AES*                                        
#  [4] print.Aggregation*                                
#  [5] print.all_vars*                                   
#  [6] print.AnnotatedPlainTextDocument*                 
#  [7] print.Annotation*                                 
#  [8] print.Annotator*                                  
#  [9] print.Annotator_Pipeline*                         
# [10] print.anova*      
# ......
length(methods(print))
# [1] 450

  使用函数 methods 打印出 print 泛型函数的部分实现函数,其实现函数有450个之多,由此可见这种泛型的定义多么重要,不需要记住那么多函数名,当然如果你记忆力惊人的化也无不可。
  S3系统具体应用不做讨论,所有基础包都是基于这个系统设计,说到面向对象只是为了引出一个被忽略的对象赋值问题。

  • 对象赋值
      = <- 均可完成对对象的赋值。与编译型语言不同,需要使用关键词 new 来为对象开辟内存空间,R对象在使用赋值符号的同时就完成了创建。 两者区别在于对函数传参时,前者为单纯的传递参数,后者会在全局环境中创建一个和该参数同名的对象。
      <<- assign 是两种被忽略的创建对象的方式,以下例子说明二者用法。
    eg:
###
# 创建函数,<- 函数内部创建对象
f <- function(x)x<-1
# 调用函数
f(1)
查找对象x
x
# 错误: 找不到对象'x'
## 原因: 函数调用结束后,其中内部对象被释放
###
# 创建函数,<<- 函数内部创建对象
sf <- function(x)x<<-1
# 调用函数
sf(1)
查找对象x
x
# [1] 1
## 原因: <<- 会在全局环境创建对象,故对象在全局环境中,未被释放

  这里我一直以为 <<- 赋值是将会创建对象在上层环境,后来在发现我错了,它会创建对象到全局环境。这时候 assign 就发挥出它的作用了。

# 创建函数sf
sf <- function(x)x<<-2*x
# 创建函数g,其内部调用函数sf
g <- function(x){sf(x);print(ls());print(x)}
# 调用函数g
g(1)
# [1] "x"
# [1] 1
# 查看全局是否有x对象
x
# [1] 2

  解析: 这里发现g函数内部调用sf函数时,其中内部的对象实际是传入的参数x,因为sf函数定义是改变上层环境的值,可是打印出的x依然等于传入的参数值,再查看全局环境,发现存在对象x,并且符合sf函数的计算规则,x等于2,故而发现<<-并未能改变其上层环境的值,而是直接跳到全局环境。

# 创建函数nsf
nsf <- function(x)assign("x", 2 * x, envir = parent.frame())
# 创建函数ng
ng <- function(x){nsf(x);print(ls());print(x)}
# 调用函数ng
ng(1)
# [1] "x"
# [1] 2
# 查看全局是否有x对象
x
# 错误: 找不到对象'x'

  解析: 这里在使用 assign 函数赋值时,使用envir参数设置了一下父类环境,其创建的对象就在上层环境当中了。

猜你喜欢

转载自blog.csdn.net/qq_24834541/article/details/78247529