R Language Object-oriented programming based on S4

Source: Internet
Author: User
Tags class definition

Objective

This article continues with an article on the R language based on S3 object-oriented programming, this paper continued to introduce the R language based on S4 object-oriented programming.

The S4 object system has obvious structural features and is more suitable for object-oriented programming. Bioconductor community, with S4 object system as the infrastructure, only accept r packages that conform to the S4 definition.

Directory

    1. S4 Object Introduction
    2. Create a S4 object
    3. Accessing the properties of an object
    4. Generic functions for S4
    5. To view the functions of a S4 object
    6. Use of S4 objects
1 S4 Object Introduction

S4 Object system is a standard object-oriented implementation of R language, S4 object has explicit class definition, parameter definition, parameter checking, inheritance relation, instantiation and other object-oriented system characteristics.

2 Creating a S4 Object

The system environment of this article

    • Linux:ubuntu Server 12.04.2 LTS 64bit
    • r:3.0.1 X86_64-pc-linux-gnu

To make it easier for us to examine the types of objects, introduce the Pryr package as an auxiliary tool. For an introduction to the Pryr package, please refer to the article: [Advanced Toolkit for Pry R-Core Pryr] (http://blog.fens.me/r-pryr/)

# 加载pryr包> library(pryr)

2.1 How do I create a S4 object?

Since the S4 object is a standard object-oriented implementation, there is a specialized class definition function SetClass () and the instantiation function of the class new (), let's see how SetClass () and new () are acting.

2.1.1 SetClass ()

View the function definitions for SetClass

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

Parameter list:

    • Class: Defines the classes name
    • Slots: Defining properties and Property types
    • Prototype: Defining default values for properties
    • Contains=character (): Define parent class, inheritance relationship
    • Validity: Defining type checking for attributes
    • Where: Define storage space
    • Sealed: If set to true, the class with the same name cannot be defined again
    • Package: Define the packages that belong to
    • s3methods:r3.0.0 not recommended for use after
    • Representation R3.0.0 not recommended for use after
    • Access R3.0.0 is not recommended for use later
    • Version R3.0.0 is not recommended for use later

2.2 Creating an S4 object instance

# 定义一个S4对象> setClass("Person",slots=list(name="character",age="numeric"))# 实例化一个Person对象> father<-new("Person",name="F",age=44)# 查看father对象,有两个属性name和age> fatherAn 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 Creating an S4 object that has an inheritance relationship

  # Create a S4 object person> setclass ("Person", Slots=list (name= "character", age= "Numeric")) # Create a subclass of person > SetClass ("Son", Slots=list (father= "person", mother= "person"), contains= "person") # instantiates the Person object > Father<-new (" Person ", name=" F ", age=44) > mother<-new (" Person ", name=" M ", age=39) # Instantiate a Son object > son<-new (" Son ", Name=" S ", Age=16,father=father,mother=mother) # View the Name property of the Son object > [email protected][1] "S" # View the Age property of the Son object > [email  protected][1] 16# View Father properties of Son object > [Email protected]an object of Class "person" slot "name": [1] "F" slot " Age ": [1] 44# view son object's mother properties > Slots (son," Mother ") an object of class ' person ' slot" name ": [1]" M "slot" Age ": [1] 39# check son Type > Otype (son) [1] "S4" # Check [email protected] Property type > Otype ([email protected]) [1] "Primitive" # check [email  protected] Attribute type > Otype ([email protected]) [1] "S4" # with IsS4 (), check the type of S4 object > IsS4 (son) [1] true> isS4 ([ Email protected]) [1] false> isS4 ([email protected]) [1] TRUE  

2.4 Default values for S4 objects

> setClass("Person",slots=list(name="character",age="numeric"))# 属性age为空> a<-new("Person",name="a")> aAn 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> bAn object of class "Person"Slot "name":[1] "b"Slot "age":[1] 20

2.5 type checking for S4 objects

> 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 ([email protected] <= 0) stop("Age is negative.")+ })Class "Person" [in ".GlobalEnv"]Slots:Name:       name       ageClass: character   numeric# 修传入小于0的年龄> bad2<-new("Person",name="bad",age=-1)Error in validityMethod(object) : Age is negative.

2.6 Creating a new object from an already instantiated object

The S4 object also supports the creation of new objects from an already instantiated object, which can overwrite the values of old objects when created

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

In the S3 object, I typically use $ to access the properties of an object, but in the S4 object, we can only use @ To access the properties of an object

> setClass("Person",slots=list(name="character",age="numeric"))> a<-new("Person",name="a")# 访问S4对象的属性> [email protected][1] "a"> slot(a, "name")[1] "a"# 错误的属性访问> a$nameError 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 Generic functions

S4 's generic function implementation differs from the implementation of S3, S4 separates the definition and implementation of the method, such as interfaces and implementations that we often say in other languages. Define the interface through Setgeneric () and define the real class by Setmethod (). This allows the S4 object system to be more consistent with object-oriented features.

Definition and invocation of common functions

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

Let me see how to separate interfaces and realities with 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([email protected] , "is working") )[1] "work"# 创建一个Person对象a> a<-new("Person",name="Conan",age=16)# 把对象a传入work函数> work(a)Conan is working

Through the S4 object system, the original function is defined and called 2 steps, in order to become 4 steps:

    • Defining data Object Types
    • Defining interface Functions
    • Defining implementation functions
    • The data object is passed to the interface function with parameters, executing the implementation function

Through the S4 object system, it is a structured and complete object-oriented implementation.

5 Viewing the functions of a S4 object

When we use the S4 object for object-oriented encapsulation, we also need to be able to see the definition of the S4 object and the function definition.

Also examples of person and work in the above section

# 检查work的类型> ftype(work)[1] "s4"      "generic"# 直接查看work函数> workstandardGeneric for "work" defined from package ".GlobalEnv"function (object)standardGeneric("work")<environment: 0x2aa6b18>Methods may be defined for arguments: objectUse  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([email protected], "is working")Signatures:        objecttarget  "Person"defined "Person"> selectMethod("work", "Person")Method Definition:function (object)cat([email protected], "is working")Signatures:        objecttarget  "Person"defined "Person"# 检查Person对象有没有work函数>  existsMethod("work", "Person")[1] TRUE> hasMethod("work", "Person")[1] TRUE
6 Use of S4 objects

We then use the S4 object as an example to define a library of graphical functions.

6.1 Task One: Define the data structure and computational functions of the graphics library

Suppose the shape is the base class for the graph, including the Circle (circle) and Ellipse (Ellipse), and calculates their area (areas) and perimeter (circum).

    • Defining the data structure of a graphics library
    • Define a circular data structure and calculate area and perimeter
    • Define the data structure of the ellipse and calculate the area and perimeter

Structure:

Defining a base class shape and Circle class 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 ([email protected] <= 0) stop("Radius is negative.")+ })Class "Circle" [in ".GlobalEnv"]Slots:Name:     radius      nameClass:   numeric characterExtends: "Shape"# 创建两个圆形实例> c1<-new("Circle",name="c1")> c2<-new("Circle",name="c2",radius=5)

Define interfaces and realities for calculating area

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

Define interfaces and realities for calculating perimeter

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

The above code, we implement the definition of a circle, we implement the ellipse.

# defines the class of the ellipse, inheriting the Shape,radius parameter with a default value of C (+), which represents the ellipse's long radius and short radius > SetClass ("Ellipse", contains= "Shape", Slots=list (radius= " Numeric "), Prototype=list (Radius=c ()) # Verify the radius parameter > setvalidity (" Ellipse ", function (object) {+ if (length ([ Email protected])! = 2) Stop ("It ' s not Ellipse.") + if (length (which ([email protected]<=0)) >0) Stop ("Radius is negative.") +}) Class "Ellipse" [in]. Globalenv "]slots:name:radius nameclass:numeric characterextends:" Shape "# creates two oval instances e1,e2> e1<-new (" Elli PSE ", name=" E1 ") > E2<-new (" Ellipse ", Name=" E2 ", Radius=c (5,1)) # Calculates the function of the Oval area reality > Setmethod (" area "," Ellipse ", function (obj,...) {+ Print ("area Ellipse Method") + pi * PROD ([email protected]) +}) [1] "area" # calculates e1,e2 two oval size > areas (e1) [1] "Site Ellipse method" [1] 3.141593> (E2) [1] "Space Ellipse method" [1] 15. 70796# Calculating the ellipse perimeter function of the reality > Setmethod ("Circum", "Ellipse", function (obj,...) {+ Cat ("Ellipse circum: \ n") + 2*pi*sqrt (([EMAIL&NBSP;PROTECTED][1]^[EMAIL&NBSP;PROTECTED][2]^2)/2) +}) [1] "Circum" # calculates e1,e2 two oval perimeter > circum (E1) Ellipse circum: [1] 6.283185> circum (E2) Ellipse circum: [1] 22.65435

6.2 Task Two: reconstruction of circular and oval designs

In the previous step, we have completed the definition of circular and elliptical data structures, as well as calculating the area and perimeter of the method reality. I wonder if you have found that the circle is a special case of the ellipse?

When the long and short radii of an ellipse are equal, the two values of radius are equal, and the resulting shape is circular. With this feature, we can redesign the relationship between the circle and the ellipse. The ellipse is the parent of the circle, and the circle is the subclass of the ellipse.

Structure:

# base class shape> SetClass ("Shape", Slots=list (name= "character", shape= "character")) # ellipse inherit shape> setclass (" Ellipse ", contains=" Shape ", Slots=list (radius=" numeric "), Prototype=list (Radius=c (+), shape=" Ellipse ")) # Circle Inherits Ellipse> SetClass ("Circle", contains= "Ellipse", Slots=list (radius= "numeric"), prototype=list (radius = 1, Shape= "Circle") # define Area interface > Setgeneric ("Area", function (obj,...) standardgeneric ("area")) [1] ' area ' # Define the Ellipse implementation > Setmethod ("area", "Ellipse", function (obj,...) for area {+ Cat ("Ellipse area: \ n") + pi * PROD ([email protected]) +}) [1] "area" # defines the Circle implementation > Setmethod ("area", "Circle", function (obj,...) of area {+ Cat ("Circle area: \ n") + pi*[email protected]^2+}) [1] "area" # defines Circum interface > setgeneric ("Circum", function (obj,...) standardgeneric ("Circum")) [1] "Circum" # Define the Ellipse implementation of Circum > Setmethod ("Circum", "Ellipse", function (obj,...) {+ Cat ("Ellipse circum: \ n") + 2*pi*sqrt (([email protected][1]^[email protected][2]^2)/2) +}) [1] "CircuM "# defines Circum's Circle implementation > Setmethod (" circum "," Circle ", function (obj,...) {+ Cat ("Circle circum: \ n") + 2*pi*[email protected]+}) [1] "circum" # Create instance > e1<-new ("Ellipse", Name= "E1", Radius=c (2,5)) > c1<-new ("Circle", Name= "C1", radius=2) # Calculate the area and perimeter of the ellipse > Areas (E1) Ellipse: [1] 31.41593> circum (E1) Ellipse circum: [1] 23.92566# calculate the area and perimeter of the Circle > areas (C1) Circle area: [1] 12.56637> circum (C1) Circle circum: [1] 12.56637

The structure after our reconstruction, is not more reasonable it!!

6.3 Task Three: increase the graphics processing of the rectangle

Our graphics library, further expanded, requires the addition of rectangles and squares.

    • Define the data structure of the rectangle and calculate the area and perimeter
    • Define the data structure of a square and calculate area and perimeter
    • A square is a special case of a rectangle that defines a rectangle as the parent of a square, while a square is a subclass of a rectangle.

Structure:

# define Rectangle Rectangle, inherit shape> setclass ("Rectangle", contains= "Shape", Slots=list (edges= "numeric"), Prototype=list ( Edges=c (shape= "Rectangle")) # defines square squares, inherits Rectangle> SetClass ("Square", contains= "Rectangle", Slots=list ( edges= "Numeric"), Prototype=list (edges=1,shape= "Square") # defines the Rectangle implementation > Setmethod ("area", "Rectangle", function (obj,...) {+ Cat ("Rectangle area: \ n") + prod ([email protected]) +}) [1] "area" # defines the Square implementation of area > Setmethod ("area", "Square", function (obj,...) {+ Cat ("Square area: \ n") + [email protected]^2+}) [1] "area" # defines Circum's Rectangle implementation > Setmethod ("Circum", "Rectangle", function (obj,...) {+ Cat ("Rectangle circum: \ n") + 2*sum ([email protected]) +}) [1] "Circum" # defines Circum's Square implementation > Setmethod ("circum", "Square", function (obj,...) {+ Cat ("Square circum: \ n") + 4*[email protected]+}) [1] "circum" # Create instance > r1<-new ("Rectangle", name= "R1", Edges=c (2,5)) > s1<-new ("Square", name= "S1", edges=2) # Calculate the area and perimeter of a rectangular shape > areas (R1) ReCtangle areas: [1] 10> area (S1) square: [1] 4# calculates the size and perimeter of the square > circum (R1) Rectangle circum: [1] 14> circum (S1) Squa Re circum: [1] 8

In this way, our graphics library, has supported 4 kinds of graphics! With the object-oriented structure to design, is not the structure of the idea is very clear!!

6.4 Task four: In the base class shape, add the shape attribute and the Getshape method

Next, for all graphics in the graphics library, define the variant shape of the graph type, and then provide a getshape function to check that the shape variable is in the instance.

This requirement, if there is no object-oriented structure, then you need to add a parameter and a judgment in all the code of the graph definition, if there are 100 graphs, it is quite complicated to change them. and object-oriented programming, it is very easy to solve this requirement. We only need to change the code on the base class to implement it.

Structure:

# 重新定义基类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([email protected],"\n")+ })[1] "getShape"

In fact, it's OK to change one of these, we just need to re-instantiate the object of each graph.

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

is not very easy to do! When the code is modified only in the base class, all the graphs have corresponding properties and methods.

If we take one more step, we can modify the definition of each object to increase the default value of the Shape property.

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"))

When you instantiate an object, the attribute shape is automatically assigned a value

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

The following is the complete code implementation of the R language:

SetClass ("Shape", Slots=list (name= "character", shape= "character")) SetClass ("Ellipse", contains= "shape", slots=list (radius= "Numeric"), Prototype=list (Radius=c (), 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 (), 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 ([email protected], "\ n")}) Setgeneric ("area", function (obj,...) standardgeneric (' area ') Setmethod ("area", "Ellipse", function (obj,...) {Cat ("Ellipse area: \ n") pi * PROD ([email protected])}) Setmethod ("area", "Circle", function (obj,...) {Cat ("Circle area: \ n") pi*[email protected]^2}) Setmethod ("area", "Rectangle", function (obj,...) {Cat ("Rectangle area: \ n") prod ([email protected])}) Setmethod ("area", "Square", function (obj,...) {Cat ("Square area: \ n") [email protected]^2}) Setgeneric ("Circum", function (obj,...) standardgeneric ("Circum")) Setmethod ("Circum", "Ellipse", function (obj,...) {Cat ("Ellipse circum: \ n") 2*pi*sqrt (([email protected][1]^[email protected][2]^2)/2)}) Setmethod ("Circum", "Circle", function (obj,...) {Cat ("Circle circum: \ n") 2*pi*[email protected]}) Setmethod ("Circum", "Rectangle", function (obj,...) {Cat ("Rectangle circum: \ n") 2*sum ([email protected])}) Setmethod ("Circum", "Square", function (obj,...) {Cat ("Square circum: \ n") 4*[email protected]}) 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)

Through this example, we fully understand the use of object-oriented in R language, and the object-oriented programming of S4 object System!

In the programmer's world, all things can be abstracted into objects.

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

R Language Object-oriented programming based on S4

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.