संबंधित मानों को अनदेखा करते हुए Swift enum के लगातार केस दबाने के लिए, एक अलग केस कुंजी की तुलना करें और हर क्रम में पहला पूरा मान रखें। Mac का यह स्थानीय उदाहरण क्रम बनाए रखता है और किसी केस को दूसरे केस के बाद फिर से आने देता है।

तय करें कि डुप्लिकेट किसे माना जाएगा

मान लीजिए कि डेस्कटॉप ग्राफ़िक्स प्रीव्यू से चरणों का एक कृत्रिम क्रम बनता है: निष्क्रिय, नामित आकृति बनाना, और नामित प्रीव्यू दिखाना। आप केस के बदलावों की एक संक्षिप्त सूची चाहते हैं। लगातार आने वाले दो ड्रॉइंग चरण डुप्लिकेट माने जाएँगे, भले ही उनकी आकृतियों के नाम अलग हों।

Swift enums में हर केस के साथ अलग-अलग संबंधित मान हो सकते हैं। ये मान enum मान का हिस्सा होते हैं, इसलिए इन्हें अनदेखा करने के लिए तुलना का स्पष्ट नियम तय करना होगा। Swift भाषा गाइड इस अंतर को Enumerations में समझाती है।

इस काम के लिए, यह अनुबंध अपनाएँ:

  • पहला इनपुट मान रखें।
  • बाद के हर उस मान को दबाएँ जिसका केस पिछले क्रम के केस से मेल खाता हो।
  • हर रखे गए तत्व का पूरा संबंधित मान सुरक्षित रखें।
  • किसी अलग केस के बाद आने पर उसी केस को फिर से रखें।
  • इनपुट का क्रम बनाए रखें; खाली इनपुट से खाली परिणाम मिले।

एक उदाहरण इनपुट के लिए अपेक्षित व्यवहार नीचे दिया गया है। सभी नाम कृत्रिम हैं।

इनपुट मान निर्णय कारण
.idle रखें पहला मान
.drawing("Circle") रखें केस बदल गया
.drawing("Square") दबाएँ अब भी ड्रॉइंग
.ready("Preview A") रखें केस बदल गया
.ready("Preview B") दबाएँ अब भी तैयार
.drawing("Triangle") रखें तैयार के बाद ड्रॉइंग आया
.idle रखें केस बदल गया
.idle दबाएँ अब भी निष्क्रिय

इसलिए रखे गए ड्रॉइंग मान \"Circle\" और \"Triangle\" हैं। \"Square\" पेलोड जानबूझकर शामिल नहीं किया गया है। अगर डेस्कटॉप व्यू में हर अपडेट किया गया आकृति-नाम दिखाना ज़रूरी है, तो यह अनुबंध उन बदलावों को हटा देगा जिनकी उस व्यू को ज़रूरत है; उस उपयोग के लिए पेलोड-संवेदी तुलना चुनें।

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

ऐसे Mac का उपयोग करें जिस पर Swift डेवलपमेंट वातावरण काम कर रहा हो। Swift.org Apple प्लेटफ़ॉर्म डेवलपमेंट के लिए Xcode की सिफ़ारिश करता है और Swift Package Manager के ज़रिए कमांड-लाइन एक्ज़ीक्यूटेबल बनाने की प्रक्रिया बताता है। टूल तैयार करने की ज़रूरत हो, तो Install Swift — macOS के निर्देश अपनाएँ।

Terminal में, एक नई कार्यशील डायरेक्टरी के अंदर छोटा एक्ज़ीक्यूटेबल पैकेज बनाएँ:

mkdir EnumCaseDemo
cd EnumCaseDemo
swift package init --name EnumCaseDemo --type executable

बनाई गई एक्ज़ीक्यूटेबल सोर्स फ़ाइल की सामग्री को नीचे दिए गए पूरे प्रोग्राम से बदलें। यह प्रोग्राम Swift की स्टैंडर्ड लाइब्रेरी के प्रकारों का उपयोग करता है और इसे किसी इम्पोर्ट या पैकेज निर्भरता की ज़रूरत नहीं है। इसका इनपुट और आउटपुट मेमोरी में रहते हैं; यह रखे गए कृत्रिम चरण प्रिंट करता है।

enum PreviewStep: Equatable {
    case idle
    case drawing(String)
    case ready(String)

    enum Kind: Equatable {
        case idle
        case drawing
        case ready
    }

    var kind: Kind {
        switch self {
        case .idle:
            return .idle
        case .drawing(_):
            return .drawing
        case .ready(_):
            return .ready
        }
    }

    var label: String {
        switch self {
        case .idle:
            return "idle"
        case .drawing(let shape):
            return "drawing: \(shape)"
        case .ready(let name):
            return "ready: \(name)"
        }
    }
}

func suppressConsecutiveCases(
    _ steps: [PreviewStep]
) -> [PreviewStep] {
    var result: [PreviewStep] = []
    var previousKind: PreviewStep.Kind? = nil

    for step in steps {
        let currentKind = step.kind
        if let previous = previousKind,
           previous == currentKind {
            continue
        }

        result.append(step)
        previousKind = currentKind
    }

    return result
}

let input: [PreviewStep] = [
    .idle,
    .drawing("Circle"),
    .drawing("Square"),
    .ready("Preview A"),
    .ready("Preview B"),
    .drawing("Triangle"),
    .idle,
    .idle
]

let expected: [PreviewStep] = [
    .idle,
    .drawing("Circle"),
    .ready("Preview A"),
    .drawing("Triangle"),
    .idle
]

let output = suppressConsecutiveCases(input)

precondition(output == expected)
precondition(suppressConsecutiveCases([]).isEmpty)
precondition(
    suppressConsecutiveCases([
        .drawing("Circle"), .drawing("Square")
    ]) == [.drawing("Circle")]
)
precondition(
    suppressConsecutiveCases([
        .idle, .drawing("Circle"), .idle
    ]) == [.idle, .drawing("Circle"), .idle]
)

for step in output {
    print(step.label)
}

पैकेज को उसकी डायरेक्टरी से चलाएँ:

swift run EnumCaseDemo

बिल्ड संदेशों के बाद प्रोग्राम का अपेक्षित आउटपुट यह है:

idle
drawing: Circle
ready: Preview A
drawing: Triangle
idle

इन उदाहरणों को प्राप्त दस्तावेज़ों के आधार पर जाँचा गया; इन्हें यहाँ चलाया नहीं गया क्योंकि शोध परिवेश में Swift एक्ज़ीक्यूटेबल उपलब्ध नहीं है। शामिल preconditions अपेक्षित रखे गए पेलोड, खाली इनपुट, अलग पेलोड वाले लगातार केस और किसी बदलाव के बाद केस के फिर से आने की जाँच करते हैं।

अलग केस कुंजी क्यों काम करती है

kind प्रॉपर्टी हर पूरे enum मान को एक छोटी, पेलोड-रहित कुंजी में बदल देती है। .drawing("Circle") और .drawing("Square") दोनों से .drawing कुंजी मिलती है। फ़िल्टरिंग फ़ंक्शन इन कुंजियों की तुलना करते हुए मूल step जोड़ता है, इसलिए रखे गए संबंधित मान जस के तस रहते हैं।

इस फ़ंक्शन को केवल एक याद रखी गई कुंजी चाहिए। शुरुआत में previousKind का मान nil होता है, जिससे पहला तत्व आगे निकल जाता है। किसी तत्व को रखने के बाद, फ़ंक्शन उसका प्रकार याद रखता है। दबाए गए तत्वों का प्रकार वही होता है, इसलिए उनके लिए याद रखी गई कुंजी अपडेट करने से कोई फ़र्क नहीं पड़ता।

यह कार्यान्वयन इनपुट को एक बार पढ़ता है। इस निश्चित enum और उसकी स्थिर आकार वाली कुंजियों के लिए, यह हर अगले तत्व पर केस-कुंजी की एक तुलना करता है और लौटाए जाने वाले ऐरे के अलावा स्थिर अतिरिक्त स्थिति का उपयोग करता है। Swift के Sequence documentation में क्रमिक पुनरावृत्ति और सामान्य रैखिक ट्रैवर्सल मॉडल का वर्णन है।

पूरा PreviewStep भी Equatable के अनुरूप है, जिससे preconditions रखे गए स्ट्रिंग सहित पूरे परिणामों की जाँच कर पाते हैं। जिन enums के संबंधित मान Equatable के अनुरूप हों, Swift उनके लिए समानता खुद बना सकता है; Protocols देखें। Kind में केस की तुलना रखने से इस फ़िल्टरिंग क्रिया के लिए स्पष्ट नियम तय होता है और पूरे प्रोग्राम में समानता की परिभाषा बदलने की ज़रूरत नहीं पड़ती।

kind में मौजूद पूर्ण switch एक उपयोगी रखरखाव-जाँच भी देता है। नया केस जोड़ते समय उसकी संबंधित कुंजी और मैपिंग भी जोड़ें। ऐसी सामान्य मैपिंग से बचें जो अनजाने में नए केस को किसी मौजूदा केस के साथ समूहित कर दे। Swift enums में exhaustive switching को Enumerations में समझाता है।

Combine के साथ यही तुलना इस्तेमाल करें

अगर आपके Mac कोड में पहले से Combine publisher है, तो Apple कस्टम तुलना के लिए removeDuplicates(by:) देता है। इसका predicate true लौटाता है, जब दूसरे मान को डुप्लिकेट माना जाना चाहिए। Apple स्पष्ट रूप से इस ऑपरेटर का समर्थन तब करता है, जब वांछित तुलना आउटपुट प्रकार की सामान्य समानता से अलग हो। यह macOS 10.15 और उसके बाद के संस्करणों में उपलब्ध है। removeDuplicates(by:) देखें।

नीचे दिया गया कोड एक extension अंश है, अपने आप में पूरा प्रोग्राम नहीं। इसे ऊपर दिए गए पूरे उदाहरण के अंत में ऐसे Mac पर जोड़ें जहाँ Combine उपलब्ध हो। यह मौजूदा PreviewStep परिभाषा और input ऐरे का फिर से उपयोग करता है:

import Combine

let subscription = input.publisher
    .removeDuplicates { previous, current in
        previous.kind == current.kind
    }
    .sink { step in
        print("Combine: \(step.label)")
    }

यह वही पाँच रखे गए चरण \"Combine: \" उपसर्ग के साथ प्रिंट करता है। Predicate केस कुंजियों की तुलना करता है और publisher मूल रखे गए enum मानों को आगे भेजता है। Apple का अपना ऑपरेटर उदाहरण भी एक प्रॉपर्टी की तुलना करके दूसरी को अनदेखा करने के इसी सिद्धांत का पालन करता है। removeDuplicates(by:)

आवश्यक सदस्यता अवधि तक लौटाई गई subscription को सक्रिय रखें। Apple बताता है कि sink(receiveValue:) से लौटाए गए मान को डीलोकेट करने पर subscription समाप्त हो जाती है। यह overload उन publishers पर लागू होता है जिनका failure type Never है, जैसा कि Apple के sequence-publisher उदाहरणों में है। sink(receiveValue:)

कोड में बदलाव करने से पहले सीमाएँ जाँचें

अगर आपके पास पहले से सीमित स्थानीय सूची है, तो array फ़ंक्शन इस्तेमाल करें। अगर मान पहले से publisher के ज़रिए आ रहे हैं, तो Combine ऑपरेटर इस्तेमाल करें। यहाँ दोनों संस्करण केस-परिवर्तन के एक ही अनुबंध को दर्शाते हैं।

आवश्यकता उपयुक्त तुलना या तरीका
हर केस क्रम का पहला पूरा मान रखें kind की तुलना करें, जैसा यहाँ दिखाया गया है
संबंधित स्ट्रिंग में हुए बदलाव रखें सामान्य समानता से पूरे मानों की तुलना करें
हर क्रम का आखिरी पूरा मान रखें लंबित मान को बफ़र करें और केस बदलने पर तथा अंत में उसे जोड़ें
किसी दूसरे केस के बाद लौटने वाले केस को रखें लगातार आने वाले क्रमों की तुलना करें, पूरे इनपुट में देखे गए केसों की सूची न बनाएँ
अलग-अलग arrays को स्वतंत्र रूप से फ़िल्टर करें फ़ंक्शन को अलग-अलग कॉल करें; हर कॉल तुलना की स्थिति खाली रखकर शुरू होती है

आखिरी पेलोड रखने के लिए अलग एल्गोरिदम चाहिए, क्योंकि यह प्रोग्राम पहला मान तुरंत जोड़ देता है। इसी तरह, एक तार्किक क्रम को अलग-अलग arrays में बाँटने से तुलना की स्थिति रीसेट हो जाती है: एक बैच के अंत में आया ड्रॉइंग मान और अगले बैच की शुरुआत में आया दूसरा ड्रॉइंग मान अलग-अलग कॉल में दोनों बच जाएँगे। अगर बैचों को एक ही क्रम की तरह काम करना है, तो बैच की सीमाओं के पार पिछली कुंजी सुरक्षित रखें।

Foundation Apple प्लेटफ़ॉर्म ऐप्स के लिए collections और अन्य बुनियादी कार्यक्षमता देता है, लेकिन यह गणना पूरी तरह Swift प्रकारों से लागू की गई है। Publisher वाले रूप के लिए खास तौर पर Combine चाहिए; import Foundation जोड़ने से वह ऑपरेटर इम्पोर्ट नहीं होता। Apple इन frameworks का अलग-अलग दस्तावेज़ देता है: Foundation और removeDuplicates(by:)।

सिंक्रोनस array फ़ंक्शन को concurrency की कोई अतिरिक्त व्यवस्था भी नहीं चाहिए: उसकी तुलना-स्थिति हर invocation की अपनी होती है। अगर बाद में आप डेस्कटॉप प्रस्तुति की साझा स्थिति जोड़ते हैं, तो उस स्थिति का isolation अलग से तय करें। Apple MainActor को एक singleton actor के रूप में बताता है, जिसका executor मुख्य dispatch queue के बराबर है; डुप्लिकेट दबाने की प्रक्रिया अपने आप किसी actor का चयन नहीं करती। MainActor

यहाँ दिखाए गए स्थानीय काम के लिए मुख्य निर्णय यह है कि हर क्रम में कौन-सा पेलोड बचा रहेगा। पहला पेलोड चुनने के बाद, अलग केस कुंजी से व्यवहार को पढ़ना और सत्यापित करना आसान हो जाता है: केस बदलने पर मूल मान रखें, मेल खाने वाली लगातार कुंजियों को दबाएँ और लौटकर आने वाले केसों को आगे जाने दें।

स्रोत