จะเปรียบเทียบ enum กับค่าที่เกี่ยวข้องได้อย่างไรโดยไม่สนใจค่าที่เกี่ยวข้องใน Swift


90

หลังจากอ่านวิธีทดสอบความเท่าเทียมกันของ Swift enums กับค่าที่เกี่ยวข้องฉันได้ใช้ enum ต่อไปนี้:

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
    }
}

รหัสต่อไปนี้ใช้งานได้:

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

อย่างไรก็ตามสิ่งนี้ไม่ได้รวบรวม:

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

... และมีข้อความแสดงข้อผิดพลาดต่อไปนี้:

ตัวดำเนินการไบนารี '==' ไม่สามารถใช้กับตัวถูกดำเนินการประเภท 'CardRank' และ '(Int) -> CardRank'

ฉันสมมติว่านี่เป็นเพราะคาดว่าจะเป็นประเภทเต็มและCardRank.Numberไม่ได้ระบุประเภททั้งหมดในขณะที่CardRank.Number(2)ทำ อย่างไรก็ตามในกรณีนี้ฉันต้องการให้ตรงกับตัวเลขใด ๆ ไม่ใช่แค่เรื่องที่เจาะจง

เห็นได้ชัดว่าฉันสามารถใช้คำสั่ง switch ได้ แต่จุดรวมของการใช้ตัว==ดำเนินการคือหลีกเลี่ยงโซลูชัน verbose นี้:

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

มีวิธีใดในการเปรียบเทียบ enum กับค่าที่เกี่ยวข้องโดยไม่สนใจค่าที่เกี่ยวข้อง

หมายเหตุ:ฉันรู้ว่าฉันสามารถเปลี่ยนกรณีใน==วิธีการเป็นcase (.Number, .Number): return trueแต่แม้ว่ามันจะคืนค่าจริงอย่างถูกต้อง แต่การเปรียบเทียบของฉันก็ยังคงดูเหมือนเมื่อเทียบกับจำนวนเฉพาะ ( number == CardRank.Number(2)โดยที่ 2 เป็นค่าดัมมี่) แทนที่จะเป็นตัวเลขใด ๆ ( number == CardRank.Number).


1
คุณสามารถลดJack, Queen, King, Aceกรณีใน==การดำเนินการผู้ประกอบการที่จะเพียงแค่:case (let x, let y) where x == y: return true
อเล็กซานเด

คำตอบ:


75

แก้ไข: ดังที่ Etan ชี้ให้เห็นคุณสามารถละเว้นการ(_)จับคู่สัญลักษณ์แทนเพื่อใช้สิ่งนี้ได้อย่างหมดจด


น่าเสียดายที่ฉันไม่เชื่อว่ามีวิธีที่ง่ายกว่าswitchแนวทางของคุณใน Swift 1.2

อย่างไรก็ตามใน Swift 2 คุณสามารถใช้การif-caseจับคู่รูปแบบใหม่:

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

หากคุณต้องการหลีกเลี่ยงการใช้คำฟุ่มเฟือยคุณอาจพิจารณาเพิ่มisNumberคุณสมบัติที่คำนวณให้กับ enum ของคุณที่ใช้คำสั่ง switch ของคุณ


1
นี้เป็นที่ดีสำหรับการควบคุมการไหล assert(number == .Number)แต่ก็นะครับเมื่อคุณต้องการเพียงแค่การแสดงออกที่เรียบง่ายเช่น: ฉันได้ แต่หวังว่าสิ่งนี้จะได้รับการปรับปรุงใน Swift เวอร์ชันใหม่ ๆ = /
Jeremy

3
ยังห่วยสำหรับสิ่งต่างๆเช่น while เงื่อนไขการวนซ้ำเป็นต้นใน Swift 3 คุณสามารถลบ (_) สำหรับรหัสที่สะอาดกว่า
Etan

ขอบคุณ @Etan ฉันได้เพิ่มคำตอบนั้นแล้ว คุณสามารถละเว้นสัญลักษณ์แทนใน Swift 2 ได้เช่นกัน คำตอบนี้เขียนขึ้นก่อนที่จะมีการเผยแพร่คุณลักษณะภาษาดังนั้นฉันจึงยังไม่รู้! :-D
Ronald Martin

นอกจากนี้: หาก enum มีกรณีเดียวที่มีค่าที่เกี่ยวข้องต้องใช้รูปแบบ if-case แม้กระทั่งสำหรับกรณีที่ไม่มีค่าที่เกี่ยวข้อง
Etan

1
@PeterWarbo คุณไม่สามารถปฏิเสธด้วยไวยากรณ์การจับคู่รูปแบบนี้ คุณจะต้องถอยกลับไปสู่defaultคดีในswitchช่วงนี้
Ronald Martin

29

น่าเสียดายที่ใน Swift 1.x ไม่มีวิธีอื่นดังนั้นคุณต้องใช้switchซึ่งไม่หรูหราเท่ารุ่นของ Swift 2 ที่คุณสามารถใช้ได้if case:

if case .Number = number {
    //ignore the value
}
if case .Number(let x) = number {
    //without ignoring
}

3
น่าเศร้าที่สิ่งนี้ใช้ได้เฉพาะในifข้อความเท่านั้นไม่ใช่สำนวน
Raphael

20

ในสวิฟท์ 4.2 จะได้รับการสังเคราะห์ถ้าค่าที่เกี่ยวข้องทั้งหมดของคุณเป็นไปตามEquatable ทั้งหมดที่คุณต้องทำคือการเพิ่มEquatableEquatable

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

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


19
แม้ว่าจะเป็นการเปรียบเทียบค่าที่เกี่ยวข้อง
โจ

3

นี่เป็นแนวทางที่ง่ายกว่า:

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

ไม่จำเป็นต้องโอเวอร์โหลดตัวดำเนินการ == และการตรวจสอบประเภทการ์ดไม่จำเป็นต้องใช้ไวยากรณ์ที่สับสน:

let card = CardRank.Jack

if card == CardRank.Jack {
    print("You played a jack")
} else if !card.isFaceCard {
    print("You must play a face card!")
}

3
แม้ว่าจะไม่ตอบคำถามที่ครอบคลุม แต่ IMO นี่เป็นวิธีแก้ปัญหาที่หรูหรากว่า (ระบุตัวเลขไว้ข้าง ๆ ) ในสถานการณ์ที่คล้ายกับ OP และไม่ควรลดลง
Nathan Hosselton

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.