لدمج قيمتين من نوع Swift struct، اكتب دالة تختار مصدر كل حقل وتُرجع قيمة جديدة. يجمع هذا المثال على macOS بين نمطين اصطناعيين للوحة رسم، مع قواعد صريحة للاستبدال والإبقاء والمسح الاختياري والمدخلات غير الصالحة.

حدّد القيمة التي لها الأسبقية قبل كتابة الدمج

تتمثل مهمة القارئ في حساب نمط واحد للوحة رسم على جهاز Mac انطلاقًا من قيمتين موجودتين. سمِّ المدخلين base وincoming: تصف هذه الأسماء دوريهما، بينما يحدد عقد لكل حقل القيمة صاحبة الأسبقية. يستخدم الحساب قيمًا اصطناعية في الذاكرة فقط.

توثّق Swift البُنى بوصفها أنواعًا قيمية، وتدعم إنشاء مثيلاتها عبر المُهيئات. أما واجهة Apple الموثّقة merge(_:uniquingKeysWith:) فتخص Dictionary، وتحسم المفاتيح المكررة عبر إغلاق. بالنسبة إلى هذا النوع المخصص من البُنى، عرّف دالة دمج يعبّر تنفيذها عن قواعد الحقول المقصودة. البُنى والفئات، دمج القاموس merge(_:uniquingKeysWith:).

استخدم العقد التوضيحي التالي. هذه خيارات تصميم لهذا المثال، وليست سلوكًا توفره Swift تلقائيًا.

الحقل القاعدة النتيجة في المثال
title خذ النص الوارد، وأزل المسافات البيضاء والأسطر الجديدة من طرفيه، ثم ارفض النتيجة الفارغة تصبح " Tile Study \n" هي \"Tile Study\"
spacing احتفظ بقيمة الأساس؛ واشترط ألا تكون سالبة تتقدم قيمة الأساس 12 على القيمة الواردة 30
showsGrid خذ القيمة المنطقية الواردة كما هي تمامًا تحل القيمة الواردة false محل قيمة الأساس true
caption خذ القيمة الاختيارية الواردة كما هي تمامًا تمسح القيمة الواردة nil شرح الأساس

يميّز هذا الجدول بين ثلاث عمليات يسهل الخلط بينها: اختيار المصدر، وتوحيد صيغة القيمة المختارة، والتحقق من النتيجة. لا تُستخدم المسافات الواردة عمدًا. فهي لا تستطيع تغيير الناتج، حتى إن اختلفت قيمتها عن مسافات الأساس.

تعيد trimmingCharacters(in:) في Foundation سلسلة جديدة بعد إزالة الأحرف المطابقة من كلا الطرفين. توثّق Apple استخدام .whitespacesAndNewlines للمسافات البيضاء المحيطة؛ والسلسلة المؤلفة بالكامل من هذه الأحرف تصبح فارغة. أما رفض هذه النتيجة الفارغة فهو قاعدة إضافية نضعها نحن. Foundation: trimmingCharacters(in:).

شغّل مثالًا محليًا كاملًا على جهاز Mac

استخدم سلسلة أدوات Swift 6 لهذا المثال. يوفّر Swift.org تعليمات التثبيت على macOS عبر Swiftly، ويذكر Xcode خيارًا للتطوير على منصات Apple. افتح Terminal وشغّل swift --version للتأكد من إتاحة الأمر. إذا لم يكن متاحًا، فاتبع تعليمات التثبيت قبل المتابعة. تثبيت Swift على macOS، البدء.

احفظ البرنامج الكامل التالي باسم MergeStyles.swift في مجلد عمل. يستورد البرنامج Foundation، ويعرّف قيمتي الإدخال، وينفذ الدمج، ويتحقق من النتيجة المتوقعة، ويختبر حالتي الرفض. لا يجري الحساب أي طلبات عبر الشبكة ولا يخزّن بيانات تطبيق.

import Foundation

struct PanelStyle: Equatable, Sendable {
    let title: String
    let spacing: Int
    let showsGrid: Bool
    let caption: String?
}

enum StyleMergeError: Error {
    case emptyTitle
    case negativeSpacing
}

func mergeStyles(
    base: PanelStyle,
    incoming: PanelStyle
) throws -> PanelStyle {
    let title = incoming.title.trimmingCharacters(
        in: .whitespacesAndNewlines
    )
    guard !title.isEmpty else {
        throw StyleMergeError.emptyTitle
    }
    guard base.spacing >= 0 else {
        throw StyleMergeError.negativeSpacing
    }

    return PanelStyle(
        title: title,
        spacing: base.spacing,
        showsGrid: incoming.showsGrid,
        caption: incoming.caption
    )
}

let base = PanelStyle(
    title: "Shape Study",
    spacing: 12,
    showsGrid: true,
    caption: "Blue squares"
)
let incoming = PanelStyle(
    title: "  Tile Study \n",
    spacing: 30,
    showsGrid: false,
    caption: nil
)

let expected = PanelStyle(
    title: "Tile Study",
    spacing: 12,
    showsGrid: false,
    caption: nil
)

let result = try mergeStyles(base: base, incoming: incoming)
precondition(result == expected)
precondition(base.title == "Shape Study")
precondition(base.caption == "Blue squares")
precondition(incoming.title == "  Tile Study \n")

print(result.title)
print("spacing: \(result.spacing)")
print("grid: \(result.showsGrid)")
print("caption cleared: \(result.caption == nil)")

let blankTitle = PanelStyle(
    title: " \n\t",
    spacing: 30,
    showsGrid: false,
    caption: nil
)
do {
    _ = try mergeStyles(base: base, incoming: blankTitle)
    preconditionFailure("Expected emptyTitle")
} catch StyleMergeError.emptyTitle {
    print("Rejected blank title")
}

let negativeBase = PanelStyle(
    title: "Shape Study",
    spacing: -1,
    showsGrid: true,
    caption: "Blue squares"
)
do {
    _ = try mergeStyles(base: negativeBase, incoming: incoming)
    preconditionFailure("Expected negativeSpacing")
} catch StyleMergeError.negativeSpacing {
    print("Rejected negative spacing")
}

من المجلد الذي يحتوي على الملف، شغّل:

swift MergeStyles.swift

الناتج المتوقع معروض أدناه. لم تكن Swift متاحة في بيئة البحث، لذا فُحص البرنامج من دون تشغيله هناك.

Tile Study
spacing: 12
grid: false
caption cleared: true
Rejected blank title
Rejected negative spacing

المُهيئ هو أداة اتخاذ القرار الأساسية: تحدد كل وسيطة حقل الإدخال المختار أو قيمة وسيطة مسماة. لا يجري تعديل أي من المدخلين. تدعم دلالات القيم في Swift هذا النهج؛ فالحقول هنا نفسها أنواع قيمية. إذا أضفت لاحقًا حقلًا من نوع فئة، فلن يؤدي نسخ البنية الحاوية إلى نسخ مستقل لمثيل الفئة المشار إليه. البُنى والفئات.

يحدث التحقق قبل الإنشاء. يؤدي العنوان الوارد الفارغ إلى رمي خطأ بدلًا من استعادة عنوان الأساس بصمت، كما يؤدي تباعد الأساس السالب إلى رمي خطأ بدلًا من تقليصه بصمت إلى صفر. إذا أخفق الشرطان معًا، فسيُبلّغ عن emptyTitle أولًا لأن هذا الحارس يُنفّذ أولًا. ينبغي للجهات المستدعية معالجة الخطأ قبل استخدام النتيجة.

قرّر ما إذا كانت القيمة الاختيارية تعني المسح أم الإبقاء

يتعامل المثال الأساسي مع incoming.caption على أنها الشرح المطلوب كاملًا. لذلك تعني nil أن «الناتج بلا شرح». وهذا يجعل المسح صريحًا في العقد، رغم أن التمثيل مجرد قيمة اختيارية.

قد ينص مطلب مختلف على أن nil الواردة تعني «الإبقاء على شرح الأساس». وفق هذه السياسة، تكون وسيطة الاستبدال في المُهيئ caption: incoming.caption ?? base.caption. يختار عامل Swift ?? المعامل الأيسر إذا احتوى على قيمة، وإلا استخدم المعامل الأيمن. هذا مقطع استبدال مُسمّى، وليس برنامجًا كاملًا ثانيًا. عامل الدمج مع nil.

لا يمكن لسياسة الرجوع هذه أن تستخدم أيضًا nil الواردة نفسها لطلب المسح. حدّد المعنى قبل اعتماد التعبير. والسلسلة الفارغة قيمة متميزة أيضًا: وفق أي من السياستين، يظل الشرح الوارد \"\" شرحًا فارغًا ما لم تضف توحيدًا للصيغة صراحةً.

عندما تحتاج عملية تحرير مستقبلية إلى الأفعال الثلاثة—الإبقاء والاستبدال والمسح—مثّل الفعل على نحو منفصل. يوضح البرنامج المستقل الكامل التالي عملية خاصة بالشرح باستخدام تعداد مخصص:

enum CaptionChange {
    case keep
    case set(String?)
}

func applying(
    _ change: CaptionChange,
    to caption: String?
) -> String? {
    switch change {
    case .keep:
        return caption
    case .set(let replacement):
        return replacement
    }
}

let original: String? = "Blue squares"
precondition(applying(.keep, to: original) == original)
precondition(applying(.set("Green circles"), to: original)
    == "Green circles")
precondition(applying(.set(nil), to: original) == nil)
print("Caption actions checked")

هذا التعداد امتداد توضيحي للعقد. أبقِ على الدالة الأصلية ذات القيمتين عندما يكون المدخلان نمطين كاملين؛ وأدخل نوعًا للفعل عندما يصف المدخل تعديلات فعلية.

تحقّق من الأسبقية باستخدام قيم مختلفة عمدًا

يُظهر اختبار الدمج المفيد اختلاف الحقول المتنافسة. فإذا استخدم كلا المدخلين تباعدًا قدره 12 وفعّلا الشبكة معًا، فقد يمر اختيار المصدر الخاطئ من دون ملاحظة. يستخدم المثال بدلًا من ذلك تباعدًا قدره 12 مقابل 30، وقيمة true مقابل false، لذا تكشف النتيجة المتوقعة فيه هذه الاختيارات.

قبل تعديل الدالة، تحقّق من الحالات الحدية التالية وفق العقد المقصود:

  • يجب أن تظل false الواردة هي false؛ لا تستخدم OR المنطقية للتعبير عن الاستبدال.
  • يجب أن يمسح الشرح الوارد nil الشرح وفق السياسة الأساسية.
  • يجب إزالة المسافات البيضاء المحيطة بالعنوان، مع الإبقاء على المسافات الداخلية.
  • يجب أن يؤدي العنوان الوارد المؤلف من مسافات بيضاء فقط إلى رمي خطأ، من دون الرجوع إلى الأساس.
  • يجب أن ينجح تباعد الأساس 0؛ ويجب أن يؤدي تباعد الأساس السالب إلى رمي خطأ.
  • يجب أن يؤدي تبديل الوسيطتين إلى تطبيق القواعد على الأدوار الجديدة المعيّنة لهما.

النقطة الأخيرة مهمة لأن هذا الدمج يعتمد على الترتيب. فعند عكس مدخلي المثال، تكون النتيجة اختيار \"Shape Study\"، والتباعد 30، والشبكة true، والشرح \"Blue squares\". هذه هي نتيجة القواعد المذكورة. سمِّ كلتا الوسيطتين عند مواضع الاستدعاء كي تظل أدوارهما واضحة.

عند إضافة حقل، وسّع الجدول والمُهيئ واختبارًا يستخدم قيمًا متنافسة. إذا احتوى نمط متداخل مستقبلًا على عدة إعدادات مستقلة، فقرّر ما إذا كانت القيمة المتداخلة بأكملها ستُستبدل أم أن لكل حقل فيها أسبقية خاصة به. لا يعرّف التنفيذ الحالي سوى الحقول الأربعة المعروضة.

أبقِ التزامن منفصلًا عن سياسة الدمج

هذا الحساب متزامن: يقرأ وسيطاته، ويتحقق من القيم المختارة، ثم يعيد نتيجة أو يرمي خطأ. لا يحتاج إلى مهمة أو ممثل لتحديد الأسبقية. توثّق Swift الدوال غير المتزامنة بوصفها دوالًا يمكن تعليق تنفيذها؛ وإضافة التنفيذ غير المتزامن لن تحدد الحقل صاحب الأسبقية. التزامن.

يعلن المثال أن PanelStyle يطابق Sendable للتعبير عن إمكانية نقل قيمه عبر حدود التزامن. وتشترط Apple أن تحتوي البنية القابلة للإرسال على أعضاء قابلين للإرسال. يتعلق هذا التوافق بنقل القيم؛ أما قواعد اختيار الحقول فتأتي بالكامل من mergeStyles. Sendable.

في مهمة لوحة الرسم المحلية هذه، أبقِ العقد بجوار الدالة واستخدم مدخلات نموذجية مختلفة بوضوح. ينبغي شرح القيمة الناتجة حقلًا واحدًا في كل مرة: عنوان وارد موحّد الصيغة، وتباعد محفوظ من الأساس، وإعداد الشبكة الوارد، وشرح وارد يمكنه مسح القيمة السابقة صراحةً.

المصادر