zoukankan      html  css  js  c++  java
  • R语言S3类的理解与构建

    R语言类

    R语言的类有S3类和S4类,S3类用的比较广,创建简单粗糙但是灵活,而S4类比较精细,具有跟C++一样严格的结构。这里我们主要讲S3类。

    S3类的结构

    S3类内部是一个list,append某个list类名称,就能成为该类。list里面的内容就是我们所说的属性.
    首先创建一个list

    me <- list(seq = "ATGC", length = nchar("ATGC"))
    me
    $seq
    [1] "ATGC"
    
    $length
    [1] 4
    

    现在me这个list只属于list类

    me
    $seq
    [1] "ATGC"
    
    $length
    [1] 4
    
    attr(,"class")
    [1] "list"   "DNAseq"
    

    然后我们append 一个类名"DNAseq",就这样我们创建了一个DNAseq类,类的属性有seq和length,值为ATGC和4

    class(me) <- append(class(me), "DNAseq")
    class(me)
    [1] "list"   "DNAseq"
    

    我们可以通过普通的list的方法来获得类的属性,比如

    me$seq
    [1] "ATGC"
    me$length
    [1] 4
    

    S3类的创建

    简单直接的构建方法

    依据刚才的类的结构,我们用函数进行类的构建,函数的输入是要传入进行类的初始化的值,而函数的返回就是新生成的类。这样我们就可以根据不同的初始化值进行类的实例化。
    首先构造一个类

    # Straight forward approach
    DNAseq <- function(seq = "ATGCATGCATGCATGCATGC"){
      me <- list(
        seq = seq,
        length = nchar(seq)
      )
      # Set the name for the class
      class(me) <- append(class(me), "DNAseq")
      return(me)
    }
    

    类的实例

    seq1 <- DNAseq()
    seq1
    
    $seq
    [1] "ATGCATGCATGCATGCATGC"
    
    $length
    [1] 20
    
    attr(,"class")
    [1] "list"   "DNAseq"
    

    局部环境构建类的方法

    当然本质还是list,但是巧妙的利用了函数运行时的局部环境。函数运行时,内部的环境是和外界隔离的,在函数内创建的变量不会影响函数外。而这种方法巧妙的取出了这个内部环境的指针,并且将它放到了list里面。最后append类名。在环境里面存放了list的指针,而在list里面又存放了环境的指针。之所以内部环境没有消失,我猜想是因为返回的类里面具有环境的指针的引用,所以内存没有释放,是一个智能指针,当然,我没有对这深究。这次属性并不是直接存放在list里面,而是存放在函数里面的环境中。而list里面放着:方法和当前环境的指针。assign是对环境中某个变量赋值,可以用get函数中获得环境中变量的值。

    # Local enviroment approach
    DNASeq <- function(seq = "ATGCATGCATGCATGCATGC"){
      ## Get the enviroment for this
      thisEnv <- environment()
      
      seq <- seq
      length <- nchar(seq)
      
      ## Create the list used to represent the
      ## object for this class
      me <- list(
        ## Define the enviroment where this list is defined so
        ## that I can refer to it
        thisEnv = thisEnv,
        
        ## Method to refer to the current enviroment
        getEnv = function(){
          return(get("thisEnv", thisEnv))
        }
      )
        
        ## Define the value of list within the 
        ## current enviroment
        assign("this", me, envir = thisEnv)
        
        ##Set the name for the class
        class(me) <- append(class(me), "DNASeq")
        return(me)
    }
    

    实例化

    seq2 <- DNASeq()
    seq2
    $thisEnv
    <environment: 0x8e86a20>
    
    $getEnv
    function () 
    {
        return(get("thisEnv", thisEnv))
    }
    <environment: 0x8e86a20>
    
    $getseq
    function () 
    {
        return(get("seq", thisEnv))
    }
    <environment: 0x8e86a20>
    
    $reverseComplement
    function () 
    {
        print("Calling the reverseComplement function of DNASeq class")
        to_base <- c("A", "T", "G", "C")
        names(to_base) <- c("T", "A", "C", "G")
        trans_seq_vect <- to_base[unlist(strsplit(get("seq", thisEnv), 
            split = ""))]
        trans_rev_vect <- trans_seq_vect[length(trans_seq_vect):1]
        newseq <- paste0(trans_rev_vect, collapse = "")
        return(DNASeq(newseq))
    }
    <environment: 0x8e86a20>
    
    attr(,"class")
    [1] "list"   "DNASeq"
    

    获得里面的seq属性的值,这里使用get获得环境中的变量的值

    get("seq", seq2$getEnv())
    [1] "ATGCATGCATGCATGCATGC"
    

    当然,如果使用这种方法生成的类,我们获得属性通常不再函数外用get,因为这样并不像面向对象的用法,我们会在给类一个方法,某个类调用这个方法之后就可以获得某个属性的值,比如可以在list中再写一个函数,getseq,就等于get("seq", thisEnv),这样就可以面向对象的使用seq2$getseq()来获得seq属性。当我们列表中添加方法时,注意应该用遵循列表的格式,用",”分开不同的方法或者不同的值。

    创建方法

    类中除了含有属性外,肯定还得含有方法。上面我们讲到用局部环境变量创建S3类时可以在list里面存放方法。当然还有一种比较普遍的,在两种方式创建的S3类中都能使用的创建方法的途径。使用某方法.某类来创建某类的方法。比如print.gg就是对gg类的print的方法。但是在创建这种方法之前我们首先得用这个方法的名字创建一个函数,这样运行函数时首先进入这个函数,然后在函数里面使用useMethod函数,在环境中寻找该类的该方法。虽然下面的代码比较复杂,但是重点时看UseMethod。

    # Creating methods
    reverseComplement <- function(object){
      UseMethod("reverseComplement", object)
    }
    reverseComplement.default <- function(object){
      print("The class of this object can not be found")
    }
    
    # Straight forward approach
    #
    # For S3 classes created by Straight forward approach
    reverseComplement.DNAseq <- function(object){
      print("Calling the reverseComplement function of DNAseq class")
      ## Compelement according to the vector below
      to_base <- c("A", "T", "G", "C")
      names(to_base) <- c("T", "A", "C", "G")
      ## Transform long charactor to vector and complement
      trans_seq_vect <- to_base[unlist(strsplit(object$seq, split = ""))]
      ## Reverse
      trans_rev_vect <- trans_seq_vect[length(trans_seq_vect):1]
      ## Collape to long character
      newseq <- paste0(trans_rev_vect, collapse = "")
      # Return a new DNAseq class
      return(DNAseq(newseq))
    }
    
    # For S3 classed created by local enviroment approach
    reverseComplement.DNASeq <- function(object){
      print("Calling the reverseComplement function of DNASeq class")
      ## Compelement according to the vector below
      to_base <- c("A", "T", "G", "C")
      names(to_base) <- c("T", "A", "C", "G")
      ## Transform long charactor to vector and complement
      trans_seq_vect <- to_base[unlist(strsplit(get("seq", seq2$getEnv()), split = ""))]
      ## Reverse
      trans_rev_vect <- trans_seq_vect[length(trans_seq_vect):1]
      ## Collape to long character
      newseq <- paste0(trans_rev_vect, collapse = "")
      # Return a new DNASeq class
      return(DNASeq(newseq))
    }
    
    

    上面还有一个default函数,表示默认的方法,如果该类找不到该类匹配的方法,就会使用默认方法。

    类继承

    S3类可以使用继承,在原来类的基础上再append一个新的类名即为新的类,用NextMethod可以调用下一层类的方法。
    创建一个primer类继承DNAseq类

    #inheritance 
    Primer <- function(seq = "ATGCATGCATGCATGCATGCGGCC"){
      pr <- strtrim(seq, 20)
      me <- DNAseq(pr)
      class(me) <- append(class(me), "Primer")
      return(me)
    }
    Primer1 <- Primer()
    Primer1
    $seq
    [1] "ATGCATGCATGCATGCATGC"
    
    $length
    [1] 20
    
    attr(,"class")
    [1] "list"   "DNAseq" "Primer"
    

    调用方法的时候会按照从左到右的顺序,再这个例子中,默认先调用DNAseq的方法,如果想要调用Primer类的方法,首先写一个Primer的reverseComplement方法

    # Creating methods 
    reverseComplement.Primer <- function(object){
      print("Running reverseComplement of Primer class")
    }
    

    然后在DNAseq类中调用下一类的方法,使用NextMethod

    reverseComplement.DNAseq <- function(object){
      print("Calling the reverseComplement function of DNAseq class")
      NextMethod("reverseComplement", object)
      ## Compelement according to the vector below
      to_base <- c("A", "T", "G", "C")
      names(to_base) <- c("T", "A", "C", "G")
      ## Transform long charactor to vector and complement
      trans_seq_vect <- to_base[unlist(strsplit(object$seq, split = ""))]
      ## Reverse
      trans_rev_vect <- trans_seq_vect[length(trans_seq_vect):1]
      ## Collape to long character
      newseq <- paste0(trans_rev_vect, collapse = "")
      # Return a new DNAseq class
      return(DNAseq(newseq))
    }
    
    
    reverseComplement(Primer1)
    [1] "Calling the reverseComplement function of DNAseq class"
    [1] "Running reverseComplement of Primer class"
    $seq
    [1] "GCATGCATGCATGCATGCAT"
    
    $length
    [1] 20
    
    attr(,"class")
    [1] "list"   "DNAseq"
    
    reverseComplement(seq1)
    [1] "Calling the reverseComplement function of DNAseq class"
    [1] "The class of this object can not be found"
    $seq
    [1] "GCATGCATGCATGCATGCAT"
    
    $length
    [1] 20
    
    attr(,"class")
    [1] "list"   "DNAseq"
    
  • 相关阅读:
    JZOJ_5838. 【广州市选2011一试】旅游路线 (Standard IO)
    JZOJ_4421. aplusb (Standard IO)
    JZOJ_3928. 射击 (Standard IO)
    JZOJ_3927. 可见点数 (Standard IO)
    AFO
    基础数论Ⅱ——笑容渐渐消失
    基础数论Ⅰ——数学渣默默流泪
    模板——二分
    模板——最小费用流
    模板——Dinic
  • 原文地址:https://www.cnblogs.com/ywliao/p/9285778.html
Copyright © 2011-2022 走看看