عند نمذجة بيانات لا يمكنها أن تأخذ إلا مجموعة محددة من الحالات الممكنة، مثل أيام الأسبوع أو اتجاهات البوصلة أو أزرار وحدة التحكم، فإن الأنواع الأساسية مثل Int أو String لها عيوب كبيرة.
مع الأعداد الصحيحة أو السلاسل النصية، يجب أن يتحقق الكود باستمرار من القيم غير الصالحة (مثل رمز حالة 999 أو خطأ كتابي مثل "leftt")، ولا يستطيع القرّاء من البشر معرفة ما الذي تمثله الأرقام بسهولة.
يحل Swift هذه المشكلة باستخدام التعدادات (وكثيرًا ما تُختصر إلى enums).
يُعرّف enum نوعًا مشتركًا لمجموعة من القيم المرتبطة المسماة. ولأن المترجم يعرف كل الحالات الممكنة مسبقًا، فإنه يستطيع التحقق من دقة كودك واكتماله في وقت الترجمة.
ويمكن أن تحتوي التعدادات أيضًا على طرق ومهيّئات وخصائص محسوبة.
تُعرّف التعدادات باستخدام الكلمة المفتاحية 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:). ولأنه قد تُمرَّر قيمة خام غير صالحة، فإن هذا المهيّئ يُرجع قيمة اختيارية:
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)لديك ثلاث مهام، وعليك إكمال كل واحدة منها لتوفير الوظائف المطلوبة.
LogLevel
عرّف تعداد LogLevel يحتوي على ست حالات تقابل مستويات السجل أعلاه، بالإضافة إلى حالة unknown للمستويات المفقودة أو غير القياسية.
tracedebuginfowarningerrorfatalunknownبعد ذلك، نفّذ مُهيّئ LogLevel لتحليل مستوى السجل في سطر السجل:
LogLevel("[INF]: File deleted")
// Returns LogLevel.info
LogLevel("Something went wrong!")
// Returns LogLevel.unknown
مستوى السجل في سطر السجل مطوّل جدًا.
لتقليل مساحة القرص اللازمة لتخزين أسطر السجل، طُوّرت صيغة مختصرة: "<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"
سجّل في Exercism لتتعلّم وتتقن Swift عبر 35 مفهومًا116 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.