日誌行

日誌行

學習練習

簡介

當你要為只能取特定幾種可能狀態的資料建立模型時,例如一週中的每一天、羅盤方向,或控制器上的按鈕,像Int或String這類基本型別有很大的缺點。 使用整數或字串時,你的程式碼必須不斷檢查無效的值(例如狀態碼999,或"leftt"這種拼錯的字),而人類讀者也無法輕易看出這些數字代表什麼。

Swift 用列舉(Enumerations)(通常簡稱為列舉)解決了這個問題。 列舉為一組相關的具名值定義了一個共同的型別。因為編譯器事先就知道所有可能的成員,所以能在編譯時期檢查你的程式碼是否正確且完整。 列舉也可以有方法、初始化器和計算屬性。

定義列舉

使用enum關鍵字來定義列舉,後面接著型別名稱,並在大括號內列出各個成員。 依照 Swift 的慣例,型別名稱使用UpperCamelCase,而每個成員使用lowerCamelCase:

enum NESButton {
    case up
    case down
    case left
    case right
    case a
    case b
    case select
    case start
}

你也可以把多個成員寫在同一行,用逗號分隔:

enum NESButton {
    case up, down, left, right, a, b, select, start
}

要取用某個列舉成員時,寫出列舉的型別名稱、一個點(.),再加上成員名稱。一旦型別已知,或編譯器能推斷出來,就可以省略型別名稱,改用更簡短的點語法:

var lastPressed = NESButton.up

// Swift infers the type, so you can omit the type name:
lastPressed = .down

let konamiCode: [NESButton] = [.up, .up, .down, .down, .left, .right, .left, .right, .b, .a]

方法與初始化器

列舉就像類別和結構體一樣,可以定義方法和自訂初始化器。在列舉的方法裡,self指的是目前的成員值:

enum Coin {
    case heads
    case tails

    init(_ value: Int) {
        if value.isMultiple(of: 2) {
            self = .heads
        } else {
            self = .tails
        }
    }

    func flip() -> Coin {
        switch self {
        case .heads: return .tails
        case .tails: return .heads
        }
    }
}

let tails = Coin(13)     // .tails
let heads = Coin(0)      // .heads
let flipped = heads.flip() // .tails

原始值

列舉成員可以預先填入預設值,這些值叫做原始值。 列舉中所有的原始值都必須共用相同的型別(例如Int、String或Character):

enum NESButton: Character {
    case up = "⬆️"
    case down = "⬇️"
    case left = "⬅️"
    case right = "➡️"
    case a = "🅰️"
    case b = "🅱️"
    case select = "✅"
    case start = "🚦"
}

print(NESButton.left.rawValue) // Prints "⬅️"

你可以用init(rawValue:)從原始值初始化列舉。由於可能傳入無效的原始值,這個初始化器會回傳一個 Optional:

let upButton = NESButton(rawValue: "⬆️") // Optional(NESButton.up)
let invalid = NESButton(rawValue: "🙄")  // nil

Swift 可以自動為String和Int列舉指定原始值:

  • 對String列舉來說,每個成員的原始值預設就是它的成員名稱。
  • 對Int列舉來說,原始值從0開始,除非另有指定,否則每次遞增 1。
enum Planet: Int {
    case mercury = 1, venus, earth, mars
}

print(Planet.earth.rawValue) // Prints 3

說明

在這個練習中,你將會處理日誌行。

每一行日誌都是一個格式如下的字串:"[<LVL>]: <MESSAGE>"。

以下是各種不同的日誌等級:

  • TRC(trace)
  • DBG(debug)
  • INF(info)
  • WRN(warning)
  • ERR(error)
  • FTL(fatal)

你有三個任務,必須全部完成才能提供必要的功能。

1. 建立 LogLevel 列舉

定義一個 LogLevel 列舉,其中包含六個對應上述日誌等級的 case,以及一個用於缺少或非標準日誌等級的 unknown case。

  • trace
  • debug
  • info
  • warning
  • error
  • fatal
  • unknown

2. 解析日誌等級

接著,實作 LogLevel 的初始化方法來解析日誌行的日誌等級:

LogLevel("[INF]: File deleted")
// Returns LogLevel.info
LogLevel("Something went wrong!")
// Returns LogLevel.unknown

3. 將日誌行轉換成簡短格式

日誌行的日誌等級相當冗長。 為了減少儲存這些日誌行所需的磁碟空間,我們設計了一種簡短格式:"<ENCODED_LEVEL>:<MESSAGE>"。

編碼後的日誌等級,就只是把日誌等級簡單對應到一個數字:

  • trace - 0
  • debug - 1
  • info - 4
  • warning - 5
  • error - 6
  • fatal - 7
  • unknown - 42

實作 shortFormat(message: String) -> String 方法,讓它可以將日誌行以簡短格式輸出成一個 String:

let overflow = LogLevel.error
overflow.shortFormat(message: "Stack overflow")
// Returns "6:Stack overflow"
透過 GitHub 編輯 連結會在新視窗或分頁中開啟
Swift Exercism

準備好開始 日誌行 了嗎?

註冊 Exercism,透過 35 個概念116 個練習 和真人引導來學習並精通 Swift,全部免費。