Swift के दो struct मानों को मर्ज करने के लिए, ऐसा फ़ंक्शन लिखें जो हर फ़ील्ड का स्रोत चुनकर एक नया मान लौटाए। macOS का यह उदाहरण दो कृत्रिम ड्रॉइंग-पैनल शैलियों को मिलाता है, जिसमें प्रतिस्थापन, संरक्षण, वैकल्पिक मान को साफ़ करने और अमान्य इनपुट के लिए स्पष्ट नियम हैं।

मर्ज लिखने से पहले तय करें कि किसकी प्राथमिकता होगी

पाठक का काम Mac पर दो मौजूदा मानों से एक ड्रॉइंग-पैनल शैली निकालना है। इन इनपुट को base और incoming नाम दें: ये नाम उनकी भूमिकाएँ बताते हैं, जबकि फ़ील्ड-दर-फ़ील्ड अनुबंध तय करता है कि किस मान की प्राथमिकता होगी। यह गणना केवल स्मृति में मौजूद कृत्रिम मानों का उपयोग करती है।

Swift में संरचनाओं को value type के रूप में दर्ज किया गया है और initializer के ज़रिए instance बनाए जा सकते हैं। Apple का दस्तावेज़ीकृत merge(_:uniquingKeysWith:) API Dictionary का हिस्सा है; यह closure के ज़रिए डुप्लिकेट कुंजियों का समाधान करता है। इस कस्टम struct के लिए ऐसा मर्ज फ़ंक्शन परिभाषित करें जिसका कार्यान्वयन फ़ील्ड के इच्छित नियमों को व्यक्त करे। Structures and Classes, Dictionary merge(_:uniquingKeysWith:)।

नीचे दिया गया उदाहरणात्मक अनुबंध अपनाएँ। ये इस उदाहरण के लिए तय किए गए डिज़ाइन विकल्प हैं, Swift की ओर से अपने-आप मिलने वाला व्यवहार नहीं।

फ़ील्ड नियम उदाहरण का परिणाम
title आने वाले टेक्स्ट को लें, उसके आसपास की whitespace और newline हटाएँ, फिर खाली परिणाम को अस्वीकार करें " Tile Study \n" बन जाता है \"Tile Study\"
spacing आधार मान को बनाए रखें; यह गैर-ऋणात्मक होना चाहिए आधार 12 को आने वाले 30 पर प्राथमिकता मिलती है
showsGrid आने वाले Boolean को ज्यों का त्यों लें आने वाला false, आधार true की जगह लेता है
caption आने वाले वैकल्पिक मान को ज्यों का त्यों लें आने वाला nil आधार कैप्शन साफ़ कर देता है

यह तालिका तीन ऐसे कामों में अंतर बताती है जिन्हें आसानी से एक समझ लिया जाता है: स्रोत चुनना, चुने गए मान को सामान्य रूप देना और परिणाम को मान्य ठहराना। आने वाली spacing का जानबूझकर उपयोग नहीं किया गया है। उसका मान आधार spacing से अलग हो, तब भी वह आउटपुट नहीं बदल सकती।

Foundation का trimmingCharacters(in:) दोनों सिरों से मेल खाने वाले अक्षर हटाकर नई स्ट्रिंग लौटाता है। Apple आसपास की whitespace के लिए .whitespacesAndNewlines के उपयोग का दस्तावेज़ देता है; पूरी तरह इन्हीं अक्षरों से बनी स्ट्रिंग खाली हो जाती है। उस खाली परिणाम को अस्वीकार करना हमारा अतिरिक्त नियम है। Foundation trimmingCharacters(in:)।

अपने Mac पर पूरा स्थानीय उदाहरण चलाएँ

इस उदाहरण के लिए Swift 6 टूलचेन इस्तेमाल करें। Swift.org, Swiftly के ज़रिए macOS इंस्टॉल करने के निर्देश देता है और Apple प्लेटफ़ॉर्म विकास के विकल्प के रूप में Xcode का उल्लेख करता है। Terminal खोलें और यह पुष्टि करने के लिए swift --version चलाएँ कि कमांड उपलब्ध है। अगर यह मौजूद नहीं है, तो आगे बढ़ने से पहले इंस्टॉलेशन निर्देशों का पालन करें। Install Swift on macOS, Getting Started।

नीचे दिए गए पूरे प्रोग्राम को किसी कार्यशील फ़ोल्डर में 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

Initializer निर्णय का मुख्य आधार है: हर argument चुने गए इनपुट फ़ील्ड या नामित मध्यवर्ती मान की पहचान करता है। दोनों में से किसी भी इनपुट को बदला नहीं जाता। Swift की value semantics इस तरीके के अनुकूल हैं; यहाँ फ़ील्ड स्वयं value type हैं। अगर बाद में आप class वाला फ़ील्ड जोड़ते हैं, तो संलग्न struct की कॉपी से संदर्भित class instance की अलग कॉपी नहीं बनती। Structures and Classes।

निर्माण से पहले सत्यापन होता है। खाली आने वाला शीर्षक चुपचाप आधार शीर्षक बहाल करने के बजाय त्रुटि देता है, और ऋणात्मक आधार spacing को चुपचाप शून्य पर सीमित करने के बजाय त्रुटि दी जाती है। अगर दोनों शर्तें विफल हों, तो पहले emptyTitle रिपोर्ट होता है, क्योंकि वह guard पहले चलता है। परिणाम का उपयोग करने से पहले कॉलर को त्रुटि संभालनी चाहिए।

तय करें कि वैकल्पिक मान का अर्थ साफ़ करना है या बनाए रखना

मुख्य उदाहरण incoming.caption को पूरा वांछित कैप्शन मानता है। इसलिए इसका nil अर्थ है, “परिणाम में कोई कैप्शन नहीं है।” इससे अनुबंध में साफ़ करने की क्रिया स्पष्ट होती है, भले ही उसका निरूपण केवल एक वैकल्पिक मान हो।

एक अलग आवश्यकता यह कह सकती है कि आने वाले nil का अर्थ है, “आधार कैप्शन बनाए रखें।” उस नीति के लिए प्रतिस्थापन initializer argument होगा caption: incoming.caption ?? base.caption। Swift का nil-coalescing ऑपरेटर बायाँ operand मान मौजूद होने पर चुनता है; अन्यथा दायाँ operand इस्तेमाल करता है। यह एक लेबल किया हुआ प्रतिस्थापन अंश है, दूसरा पूरा प्रोग्राम नहीं। Nil-coalescing operator।

उस fallback नीति में साफ़ करने का अनुरोध करने के लिए उसी आने वाले nil का उपयोग नहीं किया जा सकता। इस expression को अपनाने से पहले अर्थ चुनें। खाली स्ट्रिंग एक अलग मान है: दोनों में से किसी भी नीति के तहत \"\" वाला आने वाला कैप्शन खाली कैप्शन ही रहेगा, जब तक आप स्पष्ट रूप से सामान्यीकरण न जोड़ें।

जब भविष्य में किसी संपादन क्रिया को तीनों काम करने हों—बनाए रखना, बदलना और साफ़ करना—तो क्रिया को अलग से निरूपित करें। नीचे दिया गया स्वतंत्र और पूरा प्रोग्राम कस्टम enum के ज़रिए केवल कैप्शन के लिए एक ऑपरेशन दिखाता है:

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

यह enum अनुबंध का एक उदाहरणात्मक विस्तार है। जब दोनों इनपुट पूरी शैलियाँ हों, तो मूल दो-मान वाला फ़ंक्शन रखें; जब इनपुट वास्तव में संपादन बताए, तभी क्रिया का प्रकार शामिल करें।

जानबूझकर अलग मानों से प्राथमिकता जाँचें

उपयोगी मर्ज जाँच में प्रतिस्पर्धी फ़ील्ड के मान अलग होने चाहिए। अगर दोनों इनपुट में spacing 12 हो और दोनों grid सक्षम करें, तो गलत स्रोत चुनने पर भी गलती पकड़ में नहीं आएगी। इसके बजाय नमूने में spacing के लिए 12 बनाम 30 और true बनाम false का उपयोग किया गया है, ताकि अपेक्षित परिणाम उन विकल्पों को उजागर करे।

फ़ंक्शन को अपने उपयोग के अनुसार बदलने से पहले, इच्छित अनुबंध के आधार पर इन सीमांत मामलों की जाँच करें:

  • आने वाला false false ही रहना चाहिए; प्रतिस्थापन व्यक्त करने के लिए Boolean OR का उपयोग न करें।
  • मुख्य नीति के तहत आने वाला कैप्शन nil कैप्शन को साफ़ करना चाहिए।
  • शीर्षक के आसपास की whitespace हटनी चाहिए, जबकि बीच के spaces बने रहने चाहिए।
  • केवल whitespace वाला आने वाला शीर्षक आधार पर fallback किए बिना त्रुटि दे।
  • आधार spacing 0 सफल होना चाहिए; ऋणात्मक आधार spacing पर त्रुटि होनी चाहिए।
  • arguments को अदल-बदल करने पर नए निर्धारित रोल्स के अनुसार नियम लागू होने चाहिए।

आखिरी बात मायने रखती है, क्योंकि यह मर्ज क्रम पर निर्भर है। नमूने के इनपुट उलटने पर \"Shape Study\", spacing 30, grid true और कैप्शन \"Blue squares\" चुने जाते हैं। यही बताए गए नियमों का परिणाम है। कॉल साइट पर दोनों arguments को नाम दें, ताकि उनकी भूमिकाएँ स्पष्ट रहें।

फ़ील्ड जोड़ते समय तालिका, initializer और अलग-अलग मानों वाली एक जाँच का विस्तार करें। अगर भविष्य की किसी नेस्टेड शैली में कई स्वतंत्र सेटिंग हों, तो तय करें कि पूरा नेस्टेड मान बदला जाएगा या उसके हर फ़ील्ड की अपनी प्राथमिकता होगी। मौजूदा कार्यान्वयन केवल दिखाए गए चार फ़ील्ड परिभाषित करता है।

concurrency को मर्ज नीति से अलग रखें

यह गणना सिंक्रोनस है: यह अपने arguments पढ़ती है, चुने गए मानों को मान्य ठहराती है और कोई परिणाम लौटाती है या त्रुटि देती है। प्राथमिकता तय करने के लिए इसे किसी task या actor की ज़रूरत नहीं है। Swift में asynchronous फ़ंक्शन ऐसे फ़ंक्शन के रूप में दर्ज हैं जो suspend हो सकते हैं; asynchronous निष्पादन जोड़ने से यह तय नहीं होगा कि किस फ़ील्ड को प्राथमिकता मिलेगी। Concurrency।

उदाहरण PanelStyle को Sendable घोषित करता है, जिससे यह व्यक्त होता है कि उसके मान concurrency सीमाओं के पार जा सकते हैं। Apple के अनुसार sendable struct में sendable सदस्य होने चाहिए। यह conformance मानों को स्थानांतरित करने से संबंधित है; फ़ील्ड चुनने के नियम पूरी तरह mergeStyles से आते हैं। Sendable।

इस स्थानीय ड्रॉइंग-पैनल कार्य के लिए अनुबंध को फ़ंक्शन के पास रखें और स्पष्ट रूप से अलग नमूना इनपुट इस्तेमाल करें। परिणामी मान को हर फ़ील्ड के लिए समझाया जा सकना चाहिए: सामान्यीकृत आने वाला शीर्षक, सुरक्षित रखी गई आधार spacing, आने वाली grid सेटिंग और आने वाला कैप्शन, जो पिछले मान को स्पष्ट रूप से साफ़ कर सकता है।

स्रोत