第六天任务:
通过对模拟老虎机项目的改进,学会以下技能:
1)使用S3方法,它是R的面向对象的编程方法。
2)测算R代码的速度。
3)编写快速,向量化的R代码。
…………………………………………………………………………………………
昨天的play函数结果已经看到了,是:
play()
##”0” “0” “DD”
##0
而不是预料中的:
play()
## 0 0 DD
##0
想要完成第二种实现,就要通过R自带的类系统S3系统来解决,它掌管这R如何处理具有不同类的对象。一些函数会首先查询R的S3类,再根据其类属性做出相应的处理。R的S3系统由三个部分组成:属性,泛型函数和方法。
1.属性
查看属性:attributes()。
attr函数可以完成很多关于属性的操作,比如给某个对象添加属性,也可以查询某个对象包含的属性:
可以根据以上内容修改play函数,让其更好的输出。
play<-function(){
symbols<-get_symbols()
prize<-score(symbols)
attr(prize,”symbols”)<-symbols
prize}
现在play函数已经相当接近要求的输出风格了,但是还可以编写slot_display函数美化play函数。
slot_display<-function(prize){
#提取符号输出结果
symbols<-attr(prize,”symbols”)
#将所有符号压缩为一个字符串
symbols<-paste(symbols,collapse=” ”)
#用正则表达式将符号与奖金信息组合起来
#在正则表达式中\n表示另起一个新行
string<-paste(symbols,prize,sep=”\n$”)
#在控制台上显示正则表达式的结果,但是去掉其中的引号
cat(string)
}
通过注释可以大概了解各个函数的使用方法,其输出结果为:
slot_display(one_play)
##B 0 B
## $0
R的泛型函数类似与C++的函数模版,其函数会自动匹配调用对象的类型,比如print()函数就可以输出任何类型的对象。R的方法就是R的使用方法,是根据泛型函数自动分析R的类属性的方法。
泛型函数,方法和基于类的分派方式构成了R的S3系统。通俗来说,当用R中固有函数时,会调用Usemethod函数来识别对象的类属性,根据类属性的不同,选择对应的方法,调整程序的输出格式。
----------------------创建类-----------------------
可以利用S3系统为对象创建一个稳健的类,想要创建一个类,应该执行以下操作:
1.给类起一个名称。
2.给属于该类的每个对象赋class属性。
3.为属于该类的对象编写常用泛型函数的类方法。
许多R包都是按照相似的方法创建的。
----------------------利用循环计算期望值-----------------------
在计算期望值的时候我们可以分为三步:
1)列出所有可能出现的结果。
2)决定每个结果对应的值。
3)计算每个结果出现的概率。
将第二步结果乘以第三步的结果,加一起就是期望值。
R中的expand.grid函数可以方便快捷的写出n个向量元素的所有组合。
我们可以使用类似的方法来解决老虎机问题:
wheel<-c(“DD”,”7”,”BBB”,”BB”,”B”,”C”,”0”)
combos<-expand.grid(wheel,wheel,wheel,stringAsFactors=FALSE)
prob<-c(“DD”=0.03,”7”=0.03,”BBB”=0.06,”BB”=0.1,”B”=0.25,”C”=0.01,”0”=0.52)
combos$prob1<-prob[combos$Var1]
combos$prob2<-prob[combos$Var2]
combos$prob3<-prob[combos$Var3]
combos$prob<-combos$prob1* combos$prob12*combos$prob3
sum(combos$prob)
##1
for循环可以重复运行某段代码一定的次数,R的语法表示如下:
for(value in that){
this}
在R中for循环不会返回一个输出结果,并且R的佛如循环是针对一个集合来操作的,这是和大多数编程语言不同的地方。
例子: for(value in c(“MY”,”second”,”for”,”loop”)){
print(value)
}
##”MY”
##”second”
##”for”
##”loop”
接下来我们更新score函数使其能够处理钻石符号:
score<-function(symbols){
diamonds<-sum(symbols==”DD”)
cherries<-sum(symbols==”C”)
#识别情形
#因为钻石符号是百搭符号,因此只考虑没有钻石的情况
#三个符号相同以及都是杠的情况
slots<-symbols[symbols!=”DD”]
same<-length(unique(slots))==1
bars<-slots %in% c(“B”,”BB”,”BBB”)
#分配奖金值
if(diamond==3){
prize<-100
}else if(same){
payouts<-c(“7”=80,”BBB”=40,”BB”=25,”B”=10,”C”=10,”0”=0)
prize<-unname(payouts[slots[1]])
}else if(all (bars)){
prize<-5
}else if(cherries>0){
#如果有一个樱桃
#则将钻石当作樱桃
prize<-c(0,2,5)[cherries+diamonds+1]
}else{
prize<-0}
#根据钻石的数量,把奖金翻倍
prize*2^diamonds
}
我们可以编写for函数来计算每一行的score:
for(1 in1:norw(combos)){
symbols<-c(combos[i,1],combos[i,2],combos[i,3])
combos$prize[i]<-score(symbols)
}
然后计算期望值:
sum(combos$prize*combos$prob)
##0.934356
for循环在R中还有两个兄弟,while循环和repeat循环
while(condition){
code}
其中condition是一个逻辑测试,返回一个逻辑值,每次运行while之前都会运行一遍condition。并且while不会返回任何结果。
repeat更加初级,它会一直重复循环某代码直到你按Esc键终止循环或者遇到break命令。
repeat{code}
system.time函数接受一个R表达式作为输入,运行表达式并显示代码运行时长:system.time(function)
rep函数的作用是重复生成某一值或者向量,并返回一个更长的向量。
long<-rep(c(-1,1),1000000)
-----------------------------注-------------------------------
1.本学习记录来自Garrett Grolemund先生所著《Hands-On Programming with R》(中文名R语言入门与实践)一书。