R中的泛型函數(shù)是什么?

泛型函數(shù)(Generic function)。"一次編寫,多種類型通用,還不會搞混類型" 的函數(shù),就像超市的購物袋,它本身不規(guī)定裝什么(可以裝水果、零食、日用品),但你裝進(jìn)去蘋果,拿出來還是蘋果;裝進(jìn)去牛奶,拿出來還是牛奶。這個購物袋就類似泛型的作用 —— 它不限制具體裝什么,但保證你取出來的和放進(jìn)去的是同一種東西。使用java中的泛型來舉例解釋。
例如:在java中創(chuàng)建一個打印集合中的元素的方法,如果集合在創(chuàng)建的時候就指定了List中的元素類型,如果我們在不使用泛型的情況下,可以對每一種類型的集合創(chuàng)建一個方法(方法的重載)這種說法其實也不完全準(zhǔn)確,為了方便講解。

package src.Generics_;

import org.w3c.dom.ls.LSInput;

import java.util.Arrays;
import java.util.List;

public class GenericTest2 {
    public static void main(String[] args) {
        List<String> stringList = Arrays.asList("a", "b", "c");
        List<Integer> integerList = Arrays.asList(1, 2, 3);
        // Sting集合打印元素
        printStringList(stringList);
        // Integer集合打印元素
        printIntegerList(integerList);

    }

    // 這里創(chuàng)建一個打印String List中元素的函數(shù)
    public static void printStringList(List<String> lst) {
        for (String l1 : lst) {
            System.out.println(l1);
        }
    }

    // 這里再創(chuàng)建一個打印Integer List中的元素的函數(shù)
    public static void printIntegerList(List<Integer> lst) {
        for (Integer l2 : lst) {
            System.out.println(l2);

        }
    }
}

在上面我們在主函數(shù)中創(chuàng)建了兩個元素類似的集合。我們在這里使用了一個比較笨的方式,對集合元素打印的函數(shù)進(jìn)行重寫來滿足集合中不同元素類型的打印。當(dāng)然,如何我們使用泛型來實現(xiàn)呢?

package src.Generics_;

import java.util.Arrays;
import java.util.List;

public class GenericTest3 {
    public static void main(String[] args) {
        List<String> lst1 = Arrays.asList("A", "B", "C");
        List<Integer> lst2 = Arrays.asList(1, 2, 3, 4);
        // 這里我們使用泛型函數(shù)
        printList(lst1); // 打印String 集合
        printList(lst2); // 打印Integer 集合
        // 如果我們再創(chuàng)建一個新類型的集合
        List<Double> lst3 = Arrays.asList(1.1, 1.2, 1.2);
        printList(lst3);
    }

    // 這里咱們創(chuàng)建一個泛型函數(shù)
    public static <T> void printList(List<T> lst) {
        for (T item : lst) {
            System.out.println(item);
        }
    }
}

通過這個簡單的java中方法泛型的小例子,可以對泛型有一點了解。那么R中的泛型如何實現(xiàn)呢?R中在面向?qū)ο蟮臅r候有多種對象類型,不同的對象類型的泛型方法是否也不同呢?
是的,R 中的泛型方法以 S3 泛型 最為基礎(chǔ)和常用,覆蓋了大部分內(nèi)置功能;S4 泛型用于更嚴(yán)格的面向?qū)ο髨鼍埃坏谌桨ㄈ?tidyverse)會根據(jù)需求定義領(lǐng)域特定的泛型。泛型的核心作用是:對不同類型的對象,用統(tǒng)一的函數(shù)名實現(xiàn)不同的具體操作。


下面我們先介紹一下R中的S3泛型。

# 這里我們創(chuàng)建一個泛型函數(shù)
genericFun <- function(x,...){UseMethod("genericFun")}

# 指定泛型函數(shù)作用的類型
genericFun.numeric <- function(x){
  print("this is a number")
}
genericFun(10)
# 當(dāng)我們運行這個泛型函數(shù)的時候,打?。簍his is a number
# 如果我傳入的參數(shù)不是整型呢?
genericFun("S")
# 發(fā)生了報錯
# Error in UseMethod("genericFun") : 
#   no applicable method for 'genericFun' applied to an object of class "character"

S3 泛型的方法分派只看第一個參數(shù)(通常是 x)的類,其他參數(shù)的類型不影響方法匹配。S3 對方法的參數(shù)要求很靈活:方法可以只定義它需要的參數(shù),無需與泛型函數(shù)的參數(shù)完全匹配(只要能處理傳入的參數(shù)即可)。 R中的S3泛型函數(shù)好靈活啊。


S4泛型函數(shù)相較S3泛型函數(shù)更加的嚴(yán)格,那如何簡單使用呢?
S4 泛型通常與 S4 類配合使用,需先通過 setClass() 定義類(包含 slots 和繼承關(guān)系)
● 方法(Method):與特定類綁定的函數(shù),實現(xiàn)該類的具體邏輯,必須通過泛型函數(shù)調(diào)用。
● 類(Class):S4 類需顯式定義(包含 slots 結(jié)構(gòu),類似 “屬性”),具有嚴(yán)格的繼承關(guān)系。
● 分派機制:S4 支持多參數(shù)分派(根據(jù)多個參數(shù)的類匹配方法),而 S3 僅基于第一個參數(shù)。
這里我們舉個例子吧。

# S4泛型函數(shù)是依賴S4類的,這里我們先定義一個S4類
# 定義一個"S4Person"類,包含name(字符型)和age(數(shù)值型)兩個slots
setClass(
  Class = "S4Person",  # 類名
  slots = list(
    name = "character",  # slot名稱及類型約束
    age = "numeric"
  )
)

# 定義繼承類"S4Student"(繼承自"S4Person")
setClass(
  Class = "S4Student",
  slots = list(school = "character"),  # 新增school slot
  contains = "S4Person"  # 繼承自"S4Person"
)
# setGeneric() 定義泛型函數(shù),需指定函數(shù)名和參數(shù),核心是用 standardGeneric() 聲明泛型接口
# 定義泛型函數(shù)"greet",功能是向?qū)ο蟠蛘泻?setGeneric(
  name = "greet",  # 泛型函數(shù)名
  def = function(x) {  # 定義參數(shù)(至少包含要分派的對象)
    standardGeneric("greet")  # 聲明為S4泛型
  }
)

# 為"S4Person"類注冊greet方法
setMethod(
  f = "greet",  # 綁定到的泛型函數(shù)名
  signature = "S4Person",  # 方法對應(yīng)的類(單類)
  definition = function(x) {  # 方法邏輯
    paste0("Hello, I'm ", x@name, ", ", x@age, " years old.")
  }
)

# 為"S4Student"類注冊greet方法(繼承類可重寫方法)
setMethod(
  f = "greet",
  signature = "S4Student",
  definition = function(x) {
    paste0("Hello, I'm ", x@name, " from ", x@school, ".")
  }
)

# 多參數(shù)分派示例:為x="S4Person"、y="S4Student"注冊greetTwo方法
setMethod(
  f = "greetTwo",
  signature = signature(x = "S4Person", y = "S4Student"),  # 多個參數(shù)的類
  definition = function(x, y) {
    paste0(x@name, " says hi to ", y@name, " (student at ", y@school, ").")
  }
)

#### 測試greet泛型函數(shù)
# 創(chuàng)建S4Person對象
p <- new("S4Person", name = "Alice", age = 30)
greet(p)  # 調(diào)用S4Person的greet方法
#> [1] "Hello, I'm Alice, 30 years old."

# 創(chuàng)建S4Student對象
s <- new("S4Student", name = "Bob", age = 20, school = "MIT")
greet(s)  # 調(diào)用S4Student的greet方法
#> [1] "Hello, I'm Bob from MIT."

這里簡單總結(jié)一下兩者的區(qū)別。


image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 要了解一個函數(shù)的運行過程,一是查看幫助文檔,二是查看其代碼 普通函數(shù) 對于普通函數(shù),查看其源代碼是簡單的事情,如d...
    昨夜朱樓夢閱讀 345評論 0 0
  • R語言有四大類型系統(tǒng):基礎(chǔ)類型、S3類型、S4類型和RC類型。 R雖然被認(rèn)為是一種函數(shù)式語言,但是同樣支持面向?qū)ο?..
    LeoinUSA閱讀 9,972評論 1 19
  • 接R-面向?qū)ο缶幊?下面演示如何基于TimeSeries類實現(xiàn)一個WeightHistory類以記錄個人的歷史體重...
    王詩翔閱讀 1,042評論 0 1
  • 來自R大神著作《Advanced R 》練習(xí)題,來一起檢驗一下R語言知識吧!??本文參考資料:《Advanced R...
    caokai001閱讀 1,010評論 0 1
  • 本文轉(zhuǎn)載自http://blog.csdn.net/youshaoduo/article/details/5486...
    desunire閱讀 1,988評論 0 0

友情鏈接更多精彩內(nèi)容