前言

本文接上一篇文章 R语言基于S3的面向对象编程,本文继续介绍R语言基于S4的面向对象编程。

S4对象系统具有明显的结构化特征,更适合面向对象的程序设计。Bioconductor社区,以S4对象系统做为基础架构,只接受符合S4定义的R包。

目录

  1. S4对象介绍
  2. 创建S4对象
  3. 访问对象的属性
  4. S4的泛型函数
  5. 查看S4对象的函数
  6. S4对象的使用

1 S4对象介绍

S4对象系统是一种标准的R语言面向对象实现方式,S4对象有明确的类定义,参数定义,参数检查,继承关系,实例化等的面向对象系统的特征。

2 创建S4对象

本文的系统环境

  • Linux: Ubuntu Server 12.04.2 LTS 64bit
  • R: 3.0.1 x86_64-pc-linux-gnu

为了方便我们检查对象的类型,引入pryr包作为辅助工具。关于pryr包的介绍,请参考文章:[撬动R内核的高级工具包pryr](http://blog.fens.me/r-pryr/)


# 加载pryr包
> library(pryr)

2.1 如何创建S4对象?

由于S4对象是标准的面向对象实现方式, 有专门的类定义函数 setClass() 和类的实例化函数new() ,我们看一下setClass()和new()是如何动作的。

2.1.1 setClass()

查看setClass的函数定义


setClass(Class, representation, prototype, contains=character(),
validity, access, where, version, sealed, package,
S3methods = FALSE, slots)

参数列表:

  • Class: 定义类名
  • slots: 定义属性和属性类型
  • prototype: 定义属性的默认值
  • contains=character(): 定义父类,继承关系
  • validity: 定义属性的类型检查
  • where: 定义存储空间
  • sealed: 如果设置TRUE,则同名类不能被再次定义
  • package: 定义所属的包
  • S3methods: R3.0.0以后不建议使用
  • representation R3.0.0以后不建议使用
  • access R3.0.0以后不建议使用
  • version R3.0.0以后不建议使用

2.2 创建一个S4对象实例


# 定义一个S4对象
> setClass("Person",slots=list(name="character",age="numeric")) # 实例化一个Person对象
> father<-new("Person",name="F",age=44) # 查看father对象,有两个属性name和age
> father
An object of class "Person"
Slot "name":
[1] "F" Slot "age":
[1] 44 # 查看father对象类型,为Person
> class(father)
[1] "Person"
attr(,"package")
[1] ".GlobalEnv" # 查看father对象为S4的对象
> otype(father)
[1] "S4"

2.3 创建一个有继承关系的S4对象


# 创建一个S4对象Person
> setClass("Person",slots=list(name="character",age="numeric")) # 创建Person的子类
> setClass("Son",slots=list(father="Person",mother="Person"),contains="Person") # 实例化Person对象
> father<-new("Person",name="F",age=44)
> mother<-new("Person",name="M",age=39) # 实例化一个Son对象
> son<-new("Son",name="S",age=16,father=father,mother=mother) # 查看son对象的name属性
> son@name
[1] "S" # 查看son对象的age属性
> son@age
[1] 16 # 查看son对象的father属性
> son@father
An object of class "Person"
Slot "name":
[1] "F" Slot "age":
[1] 44 # 查看son对象的mother属性
> slot(son,"mother")
An object of class "Person"
Slot "name":
[1] "M" Slot "age":
[1] 39 # 检查son类型
> otype(son)
[1] "S4" # 检查son@name属性类型
> otype(son@name)
[1] "primitive" # 检查son@mother属性类型
> otype(son@mother)
[1] "S4" # 用isS4(),检查S4对象的类型
> isS4(son)
[1] TRUE
> isS4(son@name)
[1] FALSE
> isS4(son@mother)
[1] TRUE

2.4 S4对象的默认值


> setClass("Person",slots=list(name="character",age="numeric")) # 属性age为空
> a<-new("Person",name="a")
> a
An object of class "Person"
Slot "name":
[1] "a" Slot "age":
numeric(0) # 设置属性age的默认值20
> setClass("Person",slots=list(name="character",age="numeric"),prototype = list(age = 20)) # 属性age为空
> b<-new("Person",name="b") # 属性age的默认值是20
> b
An object of class "Person"
Slot "name":
[1] "b" Slot "age":
[1] 20

2.5 S4对象的类型检查


> setClass("Person",slots=list(name="character",age="numeric")) # 传入错误的age类型
> bad<-new("Person",name="bad",age="abc")
Error in validObject(.Object) :
invalid class “Person” object: invalid object for slot "age" in class "Person": got class "character", should be or extend class "numeric" # 设置age的非负检查
> setValidity("Person",function(object) {
+ if (object@age <= 0) stop("Age is negative.")
+ })
Class "Person" [in ".GlobalEnv"] Slots:
Name: name age
Class: character numeric # 修传入小于0的年龄
> bad2<-new("Person",name="bad",age=-1)
Error in validityMethod(object) : Age is negative.

2.6 从一个已经实例化的对象中创建新对象

S4对象,还支持从一个已经实例化的对象中创建新对象,创建时可以覆盖旧对象的值


> setClass("Person",slots=list(name="character",age="numeric")) # 创建一个对象实例n1
> n1<-new("Person",name="n1",age=19);n1
An object of class "Person"
Slot "name":
[1] "n1" Slot "age":
[1] 19 # 从实例n1中,创建实例n2,并修改name的属性值
> n2<-initialize(n1,name="n2");n2
An object of class "Person"
Slot "name":
[1] "n2" Slot "age":
[1] 19

3 访问对象的属性

在S3对象中,一般我使用$来访问一个对象的属性,但在S4对象中,我们只能使用@来访问一个对象的属性


> setClass("Person",slots=list(name="character",age="numeric"))
> a<-new("Person",name="a") # 访问S4对象的属性
> a@name
[1] "a"
> slot(a, "name")
[1] "a" # 错误的属性访问
> a$name
Error in a$name : $ operator not defined for this S4 class
> a[1]
Error in a[1] : object of type 'S4' is not subsettable
> a[[1]]
Error in a[[1]] : this S4 class is not subsettable

4 S4的泛型函数

S4的泛型函数实现有别于S3的实现,S4分离了方法的定义和实现,如在其他语言中我们常说的接口和实现分离。通过setGeneric()来定义接口,通过setMethod()来定义现实类。这样可以让S4对象系统,更符合面向对象的特征。

普通函数的定义和调用


> work<-function(x) cat(x, "is working")
> work('Conan')
Conan is working

让我来看看如何用R分离接口和现实


# 定义Person对象
> setClass("Person",slots=list(name="character",age="numeric")) # 定义泛型函数work,即接口
> setGeneric("work",function(object) standardGeneric("work"))
[1] "work" # 定义work的现实,并指定参数类型为Person对象
> setMethod("work", signature(object = "Person"), function(object) cat(object@name , "is working") )
[1] "work" # 创建一个Person对象a
> a<-new("Person",name="Conan",age=16) # 把对象a传入work函数
> work(a)
Conan is working

通过S4对象系统,把原来的函数定义和调用2步,为成了4步进行:

  • 定义数据对象类型
  • 定义接口函数
  • 定义实现函数
  • 把数据对象以参数传入到接口函数,执行实现函数

通过S4对象系统,是一个结构化的,完整的面向对象实现。

5 查看S4对象的函数

当我们使用S4对象进行面向对象封装后,我们还需要能查看到S4对象的定义和函数定义。

还以上节中Person和work的例子


# 检查work的类型
> ftype(work)
[1] "s4" "generic" # 直接查看work函数
> work
standardGeneric for "work" defined from package ".GlobalEnv"
function (object)
standardGeneric("work")
<environment: 0x2aa6b18>
Methods may be defined for arguments: object
Use showMethods("work") for currently available ones. # 查看work函数的现实定义
> showMethods(work)
Function: work (package .GlobalEnv)
object="Person" # 查看Person对象的work函数现实
> getMethod("work", "Person")
Method Definition:
function (object)
cat(object@name, "is working")
Signatures:
object
target "Person"
defined "Person" > selectMethod("work", "Person")
Method Definition:
function (object)
cat(object@name, "is working")
Signatures:
object
target "Person"
defined "Person" # 检查Person对象有没有work函数
> existsMethod("work", "Person")
[1] TRUE
> hasMethod("work", "Person")
[1] TRUE

6 S4对象的使用

我们接下用S4对象做一个例子,定义一组图形函数的库。

6.1 任务一:定义图形库的数据结构和计算函数

假设最Shape为图形的基类,包括圆形(Circle)和椭圆形(Ellipse),并计算出它们的面积(area)和周长(circum)。

  • 定义图形库的数据结构
  • 定义圆形的数据结构,并计算面积和周长
  • 定义椭圆形的数据结构,并计算面积和周长

如图所示结构:

定义基类Shape 和 圆形类Circle


# 定义基类Shape
> setClass("Shape",slots=list(name="character")) # 定义圆形类Circle,继承Shape,属性radius默认值为1
> setClass("Circle",contains="Shape",slots=list(radius="numeric"),prototype=list(radius = 1)) # 验证radius属性值要大等于0
> setValidity("Circle",function(object) {
+ if (object@radius <= 0) stop("Radius is negative.")
+ })
Class "Circle" [in ".GlobalEnv"]
Slots:
Name: radius name
Class: numeric character
Extends: "Shape" # 创建两个圆形实例
> c1<-new("Circle",name="c1")
> c2<-new("Circle",name="c2",radius=5)

定义计算面积的接口和现实


# 计算面积泛型函数接口
> setGeneric("area",function(obj,...) standardGeneric("area"))
[1] "area" # 计算面积的函数现实
> setMethod("area","Circle",function(obj,...){
+ print("Area Circle Method")
+ pi*obj@radius^2
+ })
[1] "area" # 分别计算c1和c2的两个圆形的面积
> area(c1)
[1] "Area Circle Method"
[1] 3.141593
> area(c2)
[1] "Area Circle Method"
[1] 78.53982

定义计算周长的接口和现实


# 计算周长泛型函数接口
> setGeneric("circum",function(obj,...) standardGeneric("circum"))
[1] "circum" # 计算周长的函数现实
> setMethod("circum","Circle",function(obj,...){
+ 2*pi*obj@radius
+ }) # 分别计算c1和c2的两个圆形的面积
[1] "circum"
> circum(c1)
[1] 6.283185
> circum(c2)
[1] 31.41593

上面的代码,我们实现了圆形的定义,下来我们实现椭圆形。


# 定义椭圆形的类,继承Shape,radius参数默认值为c(1,1),分别表示椭圆形的长半径和短半径
> setClass("Ellipse",contains="Shape",slots=list(radius="numeric"),prototype=list(radius=c(1,1))) # 验证radius参数
> setValidity("Ellipse",function(object) {
+ if (length(object@radius) != 2 ) stop("It's not Ellipse.")
+ if (length(which(object@radius<=0))>0) stop("Radius is negative.")
+ })
Class "Ellipse" [in ".GlobalEnv"]
Slots:
Name: radius name
Class: numeric character
Extends: "Shape" # 创建两个椭圆形实例e1,e2
> e1<-new("Ellipse",name="e1")
> e2<-new("Ellipse",name="e2",radius=c(5,1)) # 计算椭圆形面积的函数现实
> setMethod("area", "Ellipse",function(obj,...){
+ print("Area Ellipse Method")
+ pi * prod(obj@radius)
+ })
[1] "area" # 计算e1,e2两个椭圆形的面积
> area(e1)
[1] "Area Ellipse Method"
[1] 3.141593
> area(e2)
[1] "Area Ellipse Method"
[1] 15.70796 # 计算椭圆形周长的函数现实
> setMethod("circum","Ellipse",function(obj,...){
+ cat("Ellipse Circum :\n")
+ 2*pi*sqrt((obj@radius[1]^2+obj@radius[2]^2)/2)
+ })
[1] "circum" # 计算e1,e2两个椭圆形的周长
> circum(e1)
Ellipse Circum :
[1] 6.283185
> circum(e2)
Ellipse Circum :
[1] 22.65435

6.2 任务二:重构圆形和椭圆形的设计

上一步,我们已经完成了 圆形和椭圆形 的数据结构定义,以及计算面积和周长的方法现实。不知大家有没有发现,圆形是椭圆形的一个特例呢?

当椭圆形的长半径和短半径相等时,即radius的两个值相等,形成的图形为圆形。利用这个特点,我们就可以重新设计 圆形和椭圆形 的关系。椭圆形是圆形的父类,而圆形是椭圆形的子类。

如图所示结构:


# 基类Shape
> setClass("Shape",slots=list(name="character",shape="character")) # Ellipse继承Shape
> setClass("Ellipse",contains="Shape",slots=list(radius="numeric"),prototype=list(radius=c(1,1),shape="Ellipse")) # Circle继承Ellipse
> setClass("Circle",contains="Ellipse",slots=list(radius="numeric"),prototype=list(radius = 1,shape="Circle")) # 定义area接口
> setGeneric("area",function(obj,...) standardGeneric("area"))
[1] "area" # 定义area的Ellipse实现
> setMethod("area","Ellipse",function(obj,...){
+ cat("Ellipse Area :\n")
+ pi * prod(obj@radius)
+ })
[1] "area" # 定义area的Circle实现
> setMethod("area","Circle",function(obj,...){
+ cat("Circle Area :\n")
+ pi*obj@radius^2
+ })
[1] "area" # 定义circum接口
> setGeneric("circum",function(obj,...) standardGeneric("circum"))
[1] "circum" # 定义circum的Ellipse实现
> setMethod("circum","Ellipse",function(obj,...){
+ cat("Ellipse Circum :\n")
+ 2*pi*sqrt((obj@radius[1]^2+obj@radius[2]^2)/2)
+ })
[1] "circum" # 定义circum的Circle实现
> setMethod("circum","Circle",function(obj,...){
+ cat("Circle Circum :\n")
+ 2*pi*obj@radius
+ })
[1] "circum" # 创建实例
> e1<-new("Ellipse",name="e1",radius=c(2,5))
> c1<-new("Circle",name="c1",radius=2) # 计算椭圆形的面积和周长
> area(e1)
Ellipse Area :
[1] 31.41593
> circum(e1)
Ellipse Circum :
[1] 23.92566 # 计算圆形的面积和周长
> area(c1)
Circle Area :
[1] 12.56637
> circum(c1)
Circle Circum :
[1] 12.56637

我们重构后的结构,是不是会更合理呢!!

6.3 任务三:增加矩形的图形处理

我们的图形库,进一步扩充,需要加入矩形和正方形。

  • 定义矩形的数据结构,并计算面积和周长
  • 定义正方形的数据结构,并计算面积和周长
  • 正方形是矩形的特例,定义矩形是正方形的父类,而正方形是矩形的子类。

如图所示结构:


# 定义矩形Rectangle,继承Shape
> setClass("Rectangle",contains="Shape",slots=list(edges="numeric"),prototype=list(edges=c(1,1),shape="Rectangle")) # 定义正方形Square,继承Rectangle
> setClass("Square",contains="Rectangle",slots=list(edges="numeric"),prototype=list(edges=1,shape="Square")) # 定义area的Rectangle实现
> setMethod("area","Rectangle",function(obj,...){
+ cat("Rectangle Area :\n")
+ prod(obj@edges)
+ })
[1] "area" # 定义area的Square实现
> setMethod("area","Square",function(obj,...){
+ cat("Square Area :\n")
+ obj@edges^2
+ })
[1] "area" # 定义circum的Rectangle实现
> setMethod("circum","Rectangle",function(obj,...){
+ cat("Rectangle Circum :\n")
+ 2*sum(obj@edges)
+ })
[1] "circum" # 定义circum的Square实现
> setMethod("circum","Square",function(obj,...){
+ cat("Square Circum :\n")
+ 4*obj@edges
+ })
[1] "circum" # 创建实例
> r1<-new("Rectangle",name="r1",edges=c(2,5))
> s1<-new("Square",name="s1",edges=2) # 计算矩形形的面积和周长
> area(r1)
Rectangle Area :
[1] 10
> area(s1)
Square Area :
[1] 4 # 计算正方形的面积和周长
> circum(r1)
Rectangle Circum :
[1] 14
> circum(s1)
Square Circum :
[1] 8

这样,我们的图形库,已经支持了4种图形了!用面向对象的结构来设计,是不是结构化思路很清晰呢!!

6.4 任务四:在基类Shape中,增加shape属性和getShape方法

接下来,要对图形库的所有图形,定义图形类型的变量shape,然后再提供一个getShape函数可以检查实例中的是shape变量。

这个需求,如果没有面向对象的结构,那么你需要在所有图形定义的代码中,都增加一个参数和一个判断,如果有100图形,改起来还是挺复杂的。而面向对象的程序设计,就非常容易解决这个需求。我们只需要在基类上改动代码就可以实现了。

如图所示结构:


# 重新定义基类Shape,增加shape属性
> setClass("Shape",slots=list(name="character",shape="character")) # 定义getShape接口
> setGeneric("getShape",function(obj,...) standardGeneric("getShape"))
[1] "getShape" # 定义getShape实现
> setMethod("getShape","Shape",function(obj,...){
+ cat(obj@shape,"\n")
+ })
[1] "getShape"

其实,这样改动一个就可以了,我们只需要重实例化每个图形的对象就行了。


# 实例化一个Square对象,并给shape属性赋值
> s1<-new("Square",name="s1",edges=2, shape="Square") # 调用基类的getShape()函数
> getShape(r1)
Rectangle

是不是很容易的呢!在代码只在基类里修改了,所有的图形就有了对应的属性和方法。

如果我们再多做一步,可以修改每个对象的定义,增加shape属性的默认值。


setClass("Ellipse",contains="Shape",slots=list(radius="numeric"),prototype=list(radius=c(1,1),shape="Ellipse"))
setClass("Circle",contains="Ellipse",slots=list(radius="numeric"),prototype=list(radius = 1,shape="Circle"))
setClass("Rectangle",contains="Shape",slots=list(edges="numeric"),prototype=list(edges=c(1,1),shape="Rectangle"))
setClass("Square",contains="Rectangle",slots=list(edges="numeric"),prototype=list(edges=1,shape="Square"))

再实例化对象时,属性shape会被自动赋值


# 实例化一个Square对象
> s1<-new("Square",name="s1",edges=2) # 调用基类的getShape()函数
> getShape(r1)
Rectangle

下面是完整的R语言的代码实现:


setClass("Shape",slots=list(name="character",shape="character"))
setClass("Ellipse",contains="Shape",slots=list(radius="numeric"),prototype=list(radius=c(1,1),shape="Ellipse"))
setClass("Circle",contains="Ellipse",slots=list(radius="numeric"),prototype=list(radius = 1,shape="Circle"))
setClass("Rectangle",contains="Shape",slots=list(edges="numeric"),prototype=list(edges=c(1,1),shape="Rectangle"))
setClass("Square",contains="Rectangle",slots=list(edges="numeric"),prototype=list(edges=1,shape="Square")) setGeneric("getShape",function(obj,...) standardGeneric("getShape"))
setMethod("getShape","Shape",function(obj,...){
cat(obj@shape,"\n")
}) setGeneric("area",function(obj,...) standardGeneric("area"))
setMethod("area","Ellipse",function(obj,...){
cat("Ellipse Area :\n")
pi * prod(obj@radius)
})
setMethod("area","Circle",function(obj,...){
cat("Circle Area :\n")
pi*obj@radius^2
})
setMethod("area","Rectangle",function(obj,...){
cat("Rectangle Area :\n")
prod(obj@edges)
})
setMethod("area","Square",function(obj,...){
cat("Square Area :\n")
obj@edges^2
}) setGeneric("circum",function(obj,...) standardGeneric("circum"))
setMethod("circum","Ellipse",function(obj,...){
cat("Ellipse Circum :\n")
2*pi*sqrt((obj@radius[1]^2+obj@radius[2]^2)/2)
})
setMethod("circum","Circle",function(obj,...){
cat("Circle Circum :\n")
2*pi*obj@radius
})
setMethod("circum","Rectangle",function(obj,...){
cat("Rectangle Circum :\n")
2*sum(obj@edges)
})
setMethod("circum","Square",function(obj,...){
cat("Square Circum :\n")
4*obj@edges
}) e1<-new("Ellipse",name="e1",radius=c(2,5))
c1<-new("Circle",name="c1",radius=2) r1<-new("Rectangle",name="r1",edges=c(2,5))
s1<-new("Square",name="s1",edges=2) area(e1)
area(c1)
circum(e1)
circum(c1) area(r1)
area(s1)
circum(r1)
circum(s1)

通过这个例子,我们全面地了解了R语言中面向对象的使用,和S4对象系统的面向对象程序设计!

在程序员的世界里,世间万物都可以抽象成对象。

http://blog.fens.me/r-class-s4/

R语言基于S4的面向对象编程的更多相关文章

  1. go语言之进阶篇面向对象编程

    1.面向对象编程 对于面向对象编程的支持Go 语言设计得非常简洁而优雅.因为, Go语言并没有沿袭传统面向对象编程中的诸多概念,比如继承(不支持继承,尽管匿名字段的内存布局和行为类似继承,但它并不是继 ...

  2. Go语言基础之接口(面向对象编程下)

    1 接口 1.1 接口介绍 接口(interface)是Go语言中核心部分,Go语言提供面向接口编程,那么接口是什么? 现实生活中,有许多接口的例子,比如说电子设备上的充电接口,这个充电接口能干什么, ...

  3. Go语言——没有对象的面向对象编程

    本文译自Steve Francia在OSCON 2014的一个PPT,原作请前往:https://spf13.com/presentation/go-for-object-oriented-progr ...

  4. javascript: 基于原型的面向对象编程

    Douglas Crockford指出javascript是世界上最被误解的编程语言.由于javascript缺少常见的面向对象概念,许多程序猿认为javascript不是一个合适的语言.我在做第一个 ...

  5. 吴裕雄--天生自然 R语言开发学习:高级编程

    运行的条件是一元逻辑向量(TRUE或FALSE)并且不能有缺失(NA).else部分是可选的.如果 仅有一个语句,花括号也是可以省略的. 下面的代码片段是一个例子: plot(x, y) } else ...

  6. R语言处理大规模数据集的编程要点

    1.提高程序效率,保证执行速度 (1)尽量使用向量化运算 (2)尽量使用矩阵,必要时才使用数据框 (3)使用read.table时,尽量显式设定colClasses和nrows,设定comment.c ...

  7. Rserve详解,R语言客户端RSclient【转】

    R语言服务器程序 Rserve详解 http://blog.fens.me/r-rserve-server/ Rserve的R语言客户端RSclient https://blog.csdn.net/u ...

  8. 比较分析C++、Java、Python、R语言的面向对象特征,这些特征如何实现的?有什么相同点?

    一门课的课后题答案,在这里备份一下: 面向对象程序设计语言 –  比较分析C++.Java.Python.R语言的面向对象特征,这些特征如何实现的?有什么相同点? C++ 语言的面向对象特征: 对象模 ...

  9. R语言面向对象编程:S3和R6

    一.基于S3的面向对象编程 基于S3的面向对象编程是一种基于泛型函数(generic function)的实现方式. 1.S3函数的创建 S3对象组成:generic(generic FUN)+met ...

随机推荐

  1. AP_标准采购单付基本操作(流程)

    2014-06-04 Created By BaoXinjian

  2. 消息队列实现回射客户/服务器和 msgsnd、msgrcv 函数

    一.msgsnd 和 msgrcv 函数 #include <sys/types.h>   #include <sys/ipc.h>   #include <sys/ms ...

  3. python新手中常见疑惑及解答

    1 lambda函数 函数格式是lambda keys:express   匿名函数lambda是一个表达式函数,接受keys参数,返回表达式的值.所以不用return,也没有函数名,经常用在需要ke ...

  4. 让easyui的datagrid的field支持属性的子属性(field.childfield)

    如果不修改后台返回的数据格式,就只能修改easyui的源代码了. 首先在easyui的源代码中找到下面的部分,VS可以用 “var.*_.+=.*_.+\[.*_.+\];” 这个正则表达式来查找,会 ...

  5. C++继承 派生类中的内存布局(单继承、多继承、虚拟继承)

    今天在网上看到了一篇写得非常好的文章,是有关c++类继承内存布局的.看了之后获益良多,现在转在我自己的博客里面,作为以后复习之用. ——谈VC++对象模型(美)简.格雷程化    译 译者前言 一个C ...

  6. JVM 发生OOM的四种情况

    1.Java堆溢出:heap Java堆内存主要用来存放运行过程中所以的对象,该区域OOM异常一般会有如下错误信息;java.lang.OutofMemoryError:Javaheap space此 ...

  7. angular学习笔记(十二)-控制器

    本篇主要介绍控制器: 控制器在前面的例子中已经大量的用到,它的作用主要是: 控制某块视图,通过$scope向这块视图提供数据,函数,并且监视需要被检测的部分,当发生变化时,做出相应的动作,从而起到控制 ...

  8. cocos2d-x 数据存储

    这一章中,我们从一个小小的金币数入手,讨论了数据持久化的话题.我们尽量使用引擎提供的数据存储方法,以最大可能地适应跨平台需求.这里介绍的存储方法本质上都是基于 XML 的,对于 1 MB 以下的存储规 ...

  9. Oracle PLSQL Demo - 18.01管道function[查询零散的字段组成list管道返回]

    --PACKAGE CREATE OR REPLACE PACKAGE test_141213 is TYPE type_ref IS record( ENAME ), WORK_CITY ), SA ...

  10. JQuery中eq()和get()的区别

    众所周知,eq()方法返回的是一个JQuery对象,也就是[object Object]; get()方法返回的是DOM对象组成的数组,也就是[object HTMLLIElement]; 我们用一个 ...