[Swift] 比較含有 associated value 的列舉(Enum)是否相等

在 Swift 中,列舉(Enum)可說是相當好用的,尤其列舉又可以使用 associated value (詳情請見「Swift 的 列舉(Enum) 教學 一文),所以無論在輸入資料或是做比較時,都相當好用。但問題來了,如果我們要比較兩個列舉是否相等,而且列舉的 associated value 又是自訂型態的話,我們如何做比較呢?

swift 1

答案是很簡單的,我們先看一下下面的範例,假設我們有一個學生和老師的資料結構如下。

struct Student {
    let age: Int
    let name: String
}

struct Teacher {
    let age: Int
    let name: String
    let students: [Student]
}

而此時,我們的列舉如下

enum Employee {
    case teacher(Teacher)
    case student(Student)
}

接下來,我們宣告四個變數如下,並將列舉進行比較。

let studentA = Employee.student(Student(age: 18, name: "A"))
let studentB = Employee.student(Student(age: 13, name: "B"))
let studentC = Employee.student(Student(age: 13, name: "B"))
let teacher = Employee.teacher(Teacher(age: 30, name: "B", students: [studentA, studentB]))
let isSameStudnet = studentA == studentACopy //true
let isSameStudnet = studentB == studentACopy //false
let isSameIndentify = studentB == teacher //false

由於我們要比較列舉是否相同,所以勢必得將列舉設為 Equatable,並於列舉內加入「static func == (lhs: Self, rhs: Self)」函式,因此改寫如下。

enum Employee: Equatable {
    case teacher(Teacher)
    case student(Student)

    static func == (lhs: Self, rhs: Self) -> Bool {
        switch (lhs, rhs) {
        case let (.teacher(lhs), .teacher(rhs)):
            return lhs == rhs
        case let (.student(lhs), .student(rhs)):
            return lhs == rhs
        default:
            return false
        }
    }
}

讓我們關注下列這段程式碼,這段程式碼首先先比較兩個列舉是不是都是 teacher,如果是的話,就比較兩個 teacher 列舉值的 associated value 是否相等。如果兩個都是 student ,也一樣比較兩個 student 的 associated value 是否相等。如果兩個列舉值不同,一個是 teacher,一個是 student,那麼就會直接回傳 false。

static func == (lhs: Self, rhs: Self) -> Bool {
    switch (lhs, rhs) {
    case let (.teacher(lhs), .teacher(rhs)):
        return lhs == rhs
    case let (.student(lhs), .student(rhs)):
        return lhs == rhs
    default:
        return false
    }
}

正因如此,所以 associated value 也必須是 Equatable 型態,也因此 Student 和 Teacher 都必須實做 Equatable。

struct Student: Equatable {
    let age: Int
    let name: String
    
    static func == (lhs: Self, rhs: Self) -> Bool {
        lhs.age == rhs.age && lhs.name == rhs.name
    }
}

struct Teacher: Equatable {
    let age: Int
    let name: String
    let students: [Student]
    
    static func == (lhs: Self, rhs: Self) -> Bool {
        lhs.age == rhs.age && lhs.name == rhs.name && lhs.students == rhs.students
    }
}

或者,你可以用我比較不建議的方法

enum Employee: Equatable {
    case teacher(Teacher)
    case student(Student)

    static func == (lhs: Self, rhs: Self) -> Bool {
        switch (lhs, rhs) {
        case let (.teacher(lhs), .teacher(rhs)):
            return lhs.age == rhs.age && lhs.name == rhs.name && lhs.students == rhs.students
        case let (.student(lhs), .student(rhs)):
            return lhs.age == rhs.age && lhs.name == rhs.name
        default:
            return false
        }
    }
}

延續剛才的範例,如果我們要取出 studentA 的資料,怎麼辦呢?很簡單,只要靠 「if case」 語法就好了。

let studentA = Employee.student(Student(age: 18, name: "A"))
let studentB = Employee.student(Student(age: 13, name: "B"))
let studentC = Employee.student(Student(age: 13, name: "B"))
let teacher = Employee.teacher(Teacher(age: 30, name: "B", students: [studentA, studentB]))

func getStutdentAData() -> Student? {
    if case .student(let value) = studentA {
        return value
    }
    return nil
}

在 Swift 中,列舉真的是很實用的工具,大家一定要學會。

Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments