R语言之S3系统(详细介绍)老虎机程序输出改进
作者:互联网
S3
当初建议的老虎机程序应该以如下:
score <- function(symbols){
same <- symbols[1] == symbols[2] && symbols[2] == symbols[3]
bars <- symbols %in% c("B","BB","BBB")
if (same){
payouts <- c("DD" = 100,"7" = 80,"BBB" = 40,"BB" = 25,"B" = 10,
"C"=10,"0" = 0)
prize <- unname(payouts[symbols[1]])
}else if(all(bars)){
prize <- 5
}else{
cherries <- sum(symbols == "C")
prize <- c(0,2,5)[cherries + 1]
}
diamonds <- sum(symbols == "DD")
prize * 2^ diamonds
}
get_symbols <- function(){
wheel <- c("DD","7","BBB","BB","B","C","0")
sample(wheel, size = 3,replace =TRUE,
prob = c(0.03,0.03,0.06,0.1,0.25,0.01,0.52))
}
play <- function(){
symbols <- get_symbols()
print(symbols)
score(symbols)
}
play()
显示结果:
然而我们目前的老虎机程序显示的结果看起来没有上面的美观。
这些问题都可以通过R的S3系统得到解决。
1.S3系统
S3指的是R自带的类系统。这个系统掌管着R如何处理具有不同类的对象。一些函数会首先查询对象的S3类,再根据其类属性作出相应的响应。
print就是这样的一个函数。当你输出一个数值型向量时,print会显示一个数字。
但是,如果赋予该数字后面跟着POSIXt的s3类POSIXct,print将显示一个时间:
class(num)<-c("POSIXct","POSIXt")
print(num)
如果你使用的对象具有类属性,那么就会接触到R的S3系统。
R的S3系统有三个组成部分:属性(attribute)(尤其是class属性)、泛型函数(genericfunction)和方法(method)
2.属性
可以用attributes函数查看一个对象的属性。如果对第二部分中我们所创建的DECK数据框运行attributes函数,将会得到如下结果。
attributes(DECK)
attr接受两个参数:一个R对象和某个属性的名称(以字符串的形式)要赋予R对象具有指定名称的某个属性,需要将某个值保存到attr的输出结果。现在我们赋one_play一个名为symbols的属性,该属性包含一个字符串向量。
attr(one_play,"symbols")<-c("B","0","B")
attributes(one_play)
如果想查找某个属性的取值,同样可以利用attr函数,并提供一个R对象的名称和想要查找的属性名称。
attr(one_play,"symbols")
修改play函数,使得它在返回中奖金额的同时也能包含相应的老虎机符号的信息,这个信息被包含在一个叫作symbols的属性中将print(symbols)的冗余信息移除。
play<-function(){
symbols<-get_symbols()
print(symbols)
score(symbols)
}
play<-function( ){
symbols<-get_symbols()
prize<-score(symbols)
attr(prize,"symbols")<-symbols
prize
}#现在play函数会同时返回中奖金额及其对应的符号组合
two_play<-play()
two_play
利用structure函数,可以将生成中奖金额和设置属性值合并为一步来完成。
3.泛指函数
每次在控制台窗口显示某个输出结果时R都会调用print函数。
print不是一个普通的函数,它是泛型函数。这也就意味着,在不同的场合,print可以完成不同的任务。在显示无类属性的num时,print的显示结果如下。
如果赋给num一个类,print的显示结果便会发生改变。
4.方法
当你调用print函数时,它其实调用了一个特别的函数,叫作UseMethod.
比如说,当你向print提供一个类属性为POSIXct的对象时,UseMethod会将print函数的所有参数交给print.POSIXct函数处理。R随后会运行print.POSIXct函数并返回针对POSIXct类属性的输出结果。
print.POSIXct
如果对一个类属性为因子(factor)的对象调用print函数,UseMethod会将print的所有参数交给print.factor函数来进行处理。R随后会运行print.factor函数并返回结果。
print.POSIXct和print.factor被称为print函数的方法(method)。
这样一个由泛型函数、方法和基于类的分派方式所构成的系统就是R的S3系统。之所以叫作S3是由于它起源于S语言的第三个版本,语言S-PLUS是和R语言的前身。许多常见的R函数都是S3泛型函数,它们可以支持多种不同的类方法函数。比如说,summary和head就会调用UseMethod函数以识别对象的类属性。
S3系统使得R函数能够在不同的场合有不同的表现。可以利用S3系统进一步美化老虎机程序的输出格式。要实现这一点,首先将类属性赋给输出结果;然后针对该类属性编写一个print类方法。
方法分派
每一个S3方法的名称都包含两个部分。前一部分指明该方法对应的函数,后一部分则指明类属性。这两个部分的名称用英文句点.分隔。
比如说,处理类属性为函数(function)的print方法名print.function,处理类属性为矩阵(matrix)的summary方法名为summary.matrix,以此类推。
修改play函数,使其输出slots结果具有名slots的类属性:
play<-function(){
symbols<-get_symbols()
structure(score(symbols),symbols=symbols,class="slots")
}
}
现在每次运行play函数,其输出结果都将具有slots类属性.
5.类
可以利用R的S3系统为对象新建一个稳健的类(class)会以一致且合理的方式对待同属一类的对象。要想创建一个类,应该执行以下操作。
(1)给类起一个名称。
(2)给属于该类的每个对象赋class属性。
(3)为属于该类的对象编写常用泛型函数的类方法
可以针对某个类调用methods函数并指定想要查找的class属性作为其参数,也就是一个字符串。methods函数会返回R中已经存在的针对该类的所有方法。但要注意,如果某个R包事先没有经过加载,那么其中的方法就不会出现在methods的返回结果中。
methods(class="factor")
6.S3与调试
在尝试理解R函数时,S3系统可能会给你带来很多烦恼。如果一个函数在其代码中调用了UseMethod,那么就很难知道这个函数真正是做什么的。不过现在已经知道UseMethod本身会调用某个类属性所对应的类方法函数,因此可以直接找到这个类方法函数并检查它的源代码。这个类方法函数的名称应该符合<function.class>的语法结构,或者可能是<function.default>的结构。你还可以使用methods函数查看与某个函数或类相关的方法。
7.S4和R5
R还有另外两个可以用来创建类属性行为的系统。它们是S4系统和R5系统,后者也叫作引用类(reference class)系统。相比于S3系统,这两个系统的使用难度更大,因此也更少见。然而,它们提供了S3系统没有的防护措施。如果想学习更多关于这些系统的内容,包括如何编写和使用自定义泛型函数,推荐读一读HadleyWickham所著的新书Advanced R Programming.
标签:输出,函数,S3,系统,symbols,print,老虎机,属性 来源: https://blog.csdn.net/sjjsaaaa/article/details/110202996