Bagaimana cara membandingkan enum dengan nilai terkait dengan mengabaikan nilai terkait di Swift?

90

Setelah membaca Cara menguji kesetaraan enum Swift dengan nilai terkait , saya menerapkan enum berikut:

enum CardRank {
    case Number(Int)
    case Jack
    case Queen
    case King
    case Ace
}

func ==(a: CardRank, b: CardRank) -> Bool {
    switch (a, b) {
    case (.Number(let a), .Number(let b))   where a == b: return true
    case (.Jack, .Jack): return true
    case (.Queen, .Queen): return true
    case (.King, .King): return true
    case (.Ace, .Ace): return true
    default: return false
    }
}

Kode berikut berfungsi:

let card: CardRank = CardRank.Jack
if card == CardRank.Jack {
    print("You played a jack!")
} else if card == CardRank.Number(2) {
    print("A two cannot be played at this time.")
}

Namun, ini tidak dapat dikompilasi:

let number = CardRank.Number(5)
if number == CardRank.Number {
    print("You must play a face card!")
}

... dan itu memberikan pesan kesalahan berikut:

Operator biner '==' tidak dapat diterapkan ke operan jenis 'CardRank' dan '(Int) -> CardRank'

Saya berasumsi ini karena mengharapkan tipe penuh dan CardRank.Numbertidak menentukan tipe keseluruhan, padahal CardRank.Number(2)itu. Namun, dalam kasus ini, saya ingin mencocokkan nomor apa pun ; bukan hanya yang spesifik.

Jelas saya dapat menggunakan pernyataan sakelar, tetapi inti penerapan ==operator adalah untuk menghindari solusi verbose ini:

switch number {
case .Number:
    print("You must play a face card!")
default:
    break
}

Apakah ada cara untuk membandingkan enum dengan nilai terkait sambil mengabaikan nilai yang terkait?

Catatan: Saya menyadari bahwa saya dapat mengubah kasus dalam ==metode menjadi case (.Number, .Number): return true, tetapi, meskipun itu akan mengembalikan true dengan benar, perbandingan saya masih akan terlihat seperti dibandingkan dengan nomor tertentu ( number == CardRank.Number(2); di mana 2 adalah nilai dummy) daripada angka apa pun ( number == CardRank.Number).

Senseful
sumber
1
Anda dapat mengurangi Jack, Queen, King, Acekasus di ==pelaksanaan operator untuk hanya:case (let x, let y) where x == y: return true
Alexander

Jawaban:

75

Sunting: Seperti yang Etan tunjukkan, Anda dapat menghilangkan (_)kecocokan wildcard untuk menggunakan ini lebih bersih.


Sayangnya, saya tidak percaya bahwa ada cara yang lebih mudah daripada switchpendekatan Anda di Swift 1.2.

Namun, di Swift 2, Anda dapat menggunakan if-casepencocokan pola baru :

let number = CardRank.Number(5)
if case .Number(_) = number {
    // Is a number
} else {
    // Something else
}

Jika Anda ingin menghindari verbositas, Anda dapat mempertimbangkan untuk menambahkan isNumberproperti yang dihitung ke enum Anda yang mengimplementasikan pernyataan switch Anda.

Ronald Martin
sumber
1
Ini bagus untuk aliran kontrol, tetapi agak menyebalkan ketika Anda hanya ingin ekspresi sederhana, misalnya: assert(number == .Number). Saya hanya bisa berharap ini ditingkatkan di versi Swift yang lebih baru. = /
Jeremy
3
Juga menyebalkan untuk hal-hal seperti kondisi loop sementara dll. Di Swift 3 Anda dapat menghapus (_) untuk kode yang lebih bersih.
Etan
Terima kasih @Etan, saya telah menambahkan itu ke jawabannya. Anda sebenarnya bisa menghilangkan wildcard di Swift 2 juga. Jawaban ini ditulis sebelum fitur bahasa dirilis, jadi saya belum mengetahuinya! :-D
Ronald Martin
Selain itu: Jika enum memiliki satu kasus dengan nilai terkait, pola if-case perlu digunakan bahkan untuk kasus yang tidak memiliki nilai terkait.
Etan
1
@PeterWarbo, Anda tidak dapat melakukan negasi dengan sintaks pencocokan pola ini. Anda harus kembali ke defaultkasus di satu switchblok untuk saat ini.
Ronald Martin
29

Sayangnya di Swift 1.x tidak ada cara lain sehingga Anda harus menggunakan switchyang tidak seanggun versi Swift 2 di mana Anda dapat menggunakan if case:

if case .Number = number {
    //ignore the value
}
if case .Number(let x) = number {
    //without ignoring
}
Qbyte
sumber
3
Sayangnya, ini hanya berfungsi dalam ifpernyataan, bukan sebagai ekspresi.
Raphael
20

Di Swift 4.2 Equatableakan disintesis jika semua nilai terkait Anda sesuai Equatable. Yang perlu Anda lakukan hanyalah menambahkan Equatable.

enum CardRank: Equatable {
    case Number(Int)
    case Jack
    case Queen
    case King
    case Ace
}

https://developer.apple.com/documentation/swift/equatable?changes=_3

nambatee
sumber
19
Padahal ini akan membandingkan nilai terkait.
Joe
3

Berikut pendekatan yang lebih sederhana:

enum CardRank {
    case Two
    case Three
    case Four
    case Five
    case Six
    case Seven
    case Eight
    case Nine
    case Ten
    case Jack
    case Queen
    case King
    case Ace

    var isFaceCard: Bool {
        return (self == Jack) || (self == Queen) || (self == King)
    }
}

Tidak perlu membebani operator ==, dan memeriksa jenis kartu tidak memerlukan sintaks yang membingungkan:

let card = CardRank.Jack

if card == CardRank.Jack {
    print("You played a jack")
} else if !card.isFaceCard {
    print("You must play a face card!")
}
Mike Taverne
sumber
3
Meskipun tidak menjawab pertanyaan menyeluruh, IMO ini adalah solusi yang lebih elegan (selain Angka yang disebutkan) dalam skenario yang mirip dengan OP dan tidak boleh diturunkan.
Nathan Hosselton
0

Anda tidak membutuhkan func ==atau Equatable. Cukup gunakan pola kasus pencacahan .

let rank = CardRank.Ace
if case .Ace = rank { print("Snoopy") }
Edward Brey
sumber