वैकल्पिक बाइंडिंग के माध्यम से स्विफ्ट में सुरक्षित (सीमा-जाँच) सरणी की खोज?


271

यदि मेरे पास स्विफ्ट में एक सरणी है, और एक सूचकांक का उपयोग करने की कोशिश करते हैं, जो सीमा से बाहर है, तो एक अप्रत्याशित परिणाम है:

var str = ["Apple", "Banana", "Coconut"]

str[0] // "Apple"
str[3] // EXC_BAD_INSTRUCTION

हालाँकि, मैंने सोचा था कि स्विफ्ट लाने वाली सभी वैकल्पिक चेनिंग और सुरक्षा के साथ , यह कुछ करने के लिए तुच्छ होगा:

let theIndex = 3
if let nonexistent = str[theIndex] { // Bounds check + Lookup
    print(nonexistent)
    ...do other things with nonexistent...
}

के बजाय:

let theIndex = 3
if (theIndex < str.count) {         // Bounds check
    let nonexistent = str[theIndex] // Lookup
    print(nonexistent)   
    ...do other things with nonexistent... 
}

लेकिन यह मामला नहीं है - मुझे ifसूचकांक की जांच करने और यह सुनिश्चित करने के लिए कि सूचकांक से कम है का उपयोग करना होगा str.count

मैंने अपने स्वयं के subscript()कार्यान्वयन को जोड़ने की कोशिश की , लेकिन मुझे यकीन नहीं है कि मूल कार्यान्वयन के लिए कॉल कैसे पास किया जाए, या सबस्क्रिप्ट नोटेशन का उपयोग किए बिना आइटम (इंडेक्स-आधारित) का उपयोग किया जाए:

extension Array {
    subscript(var index: Int) -> AnyObject? {
        if index >= self.count {
            NSLog("Womp!")
            return nil
        }
        return ... // What?
    }
}

2
मुझे लगता है कि यह थोड़ा ओटी है, लेकिन मुझे यह भी अच्छा लगता है अगर स्विफ्ट में किसी भी प्रकार की सीमा की जांच करने के लिए स्पष्ट वाक्यविन्यास होता है, जिसमें सूचियां भी शामिल हैं। हमारे पास इसके लिए पहले से ही एक उपयुक्त कीवर्ड है, इसलिए उदाहरण के लिए, यदि X इन (1,2,7) ... या यदि X मेरे myArray में
मॉरी मार्कोविट्ज़

जवाबों:


652

एलेक्स के जवाब में सवाल के लिए अच्छी सलाह और समाधान है, हालांकि, मैं इस कार्यक्षमता को लागू करने के एक अच्छे तरीके पर ठोकर खाने के लिए हुआ हूं:

3.2 और नई स्विफ्ट

extension Collection {

    /// Returns the element at the specified index if it is within bounds, otherwise nil.
    subscript (safe index: Index) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

स्विफ्ट 3.0 और 3.1

extension Collection where Indices.Iterator.Element == Index {

    /// Returns the element at the specified index if it is within bounds, otherwise nil.
    subscript (safe index: Index) -> Generator.Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

स्विफ्ट 3 के समाधान के लिए आने का श्रेय हमीश को है ।

स्विफ्ट 2

extension CollectionType {

    /// Returns the element at the specified index if it is within bounds, otherwise nil.
    subscript (safe index: Index) -> Generator.Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

उदाहरण

let array = [1, 2, 3]

for index in -20...20 {
    if let item = array[safe: index] {
        print(item)
    }
}

45
मुझे लगता है कि यह निश्चित रूप से ध्यान देने योग्य है - अच्छा काम। मुझे safe:अंतर सुनिश्चित करने के लिए शामिल पैरामीटर नाम पसंद है ।
क्रेग ओटिस

11
स्विफ्ट 2 (एक्सकोड 7) के रूप में इसे थोड़ा ट्वीक करने की आवश्यकता है:return self.indices ~= index ? self[index] : nil;
टिम

7
स्विफ्ट 3 संस्करण के बारे में: संभवतः एक कोने-केस-केवल-प्रॉम्प्ट, लेकिन फिर भी एक त्वरित: ऐसे मामले हैं जहां ऊपर "सुरक्षित" सबस्क्रिप्ट संस्करण सुरक्षित नहीं है (जबकि स्विफ्ट 2 संस्करण था): Collectionप्रकारों के लिए जहां Indicesहैं सन्निहित नहीं। Setउदाहरणों के लिए, यदि हम इंडेक्स द्वारा एक निर्धारित तत्व को एक्सेस करने के लिए थे ( SetIndex<Element>), तो हम इंडेक्स के लिए रनटाइम अपवाद में दौड़ सकते हैं >= startIndexऔर < endIndexजो कि सुरक्षित सबस्क्रिप्ट में विफल रहता है (उदाहरण के लिए यह आकस्मिक उदाहरण देखें )।
18

12
चेतावनी! इस तरह से सरणियों की जाँच करना वास्तव में महंगा हो सकता है। containsविधि सभी सूचकांकों इस प्रकार यह एक हे (एन) बनाने के माध्यम से पुनरावृति होगी। एक बेहतर तरीका यह है कि सूचकांक का उपयोग करें और सीमा की जांच करें।
स्टीफन वासिलजेविक

6
सूचकांकों को उत्पन्न करने और उन पर (O (n)) को रोकने के लिए, तुलना (O (1)) का उपयोग करना बेहतर है: return index >= startIndex && index < endIndex ? self[index] : nil Collectionप्रकार हैं startIndex, endIndexजो हैं Comparable। बेशक, यह कुछ अजीब संग्रह के लिए काम नहीं करेगा, उदाहरण के लिए, बीच में सूचकांक नहीं है, साथ समाधान indicesअधिक सामान्य है।
जुबको

57

यदि आप वास्तव में इस व्यवहार को चाहते हैं, तो यह खुशबू आ रही है जैसे आप एक ऐरे के बजाय एक शब्दकोश चाहते हैं। nilगायब कुंजियों तक पहुँचने पर शब्दकोश वापस लौटता है , जो समझ में आता है क्योंकि यह जानना बहुत कठिन है कि कुंजी में कोई कुंजी मौजूद है या नहीं क्योंकि उन कुंजियों में कुछ भी हो सकता है, जहाँ एक सरणी में कुंजी को एक सीमा में होना चाहिए : 0से count। और इस श्रेणी में पुनरावृत्ति करना अविश्वसनीय रूप से सामान्य है, जहाँ आप पूरी तरह से सुनिश्चित कर सकते हैं कि लूप के प्रत्येक पुनरावृत्ति पर एक वास्तविक मूल्य हो।

मुझे लगता है कि इसका कारण यह नहीं है कि यह स्विफ्ट डेवलपर्स द्वारा बनाया गया डिज़ाइन विकल्प है। अपना उदाहरण लें:

var fruits: [String] = ["Apple", "Banana", "Coconut"]
var str: String = "I ate a \( fruits[0] )"

यदि आपको पहले से ही पता है कि सूचकांक मौजूद है, जैसा कि आप ज्यादातर मामलों में करते हैं जहां आप एक सरणी का उपयोग करते हैं, तो यह कोड बहुत अच्छा है। हालांकि, अगर एक सबस्क्रिप्ट तक पहुँचने संभवतः लौट सकते हैं nilतो आप वापसी प्रकार बदल दिया है की Arrayकी subscriptविधि एक वैकल्पिक किया जाना है। इससे आपका कोड बदल जाता है:

var fruits: [String] = ["Apple", "Banana", "Coconut"]
var str: String = "I ate a \( fruits[0]! )"
//                                     ^ Added

इसका मतलब है कि आपको हर बार एक सरणी के माध्यम से पुनरावृत्त होने पर एक वैकल्पिक को खोलना होगा, या एक ज्ञात सूचकांक के साथ और कुछ भी करना होगा, सिर्फ इसलिए कि शायद ही आप सीमा सूचकांक से बाहर पहुंच सकें। स्विफ्ट डिजाइनरों ने सीमा इंडेक्स से बाहर पहुंचने पर एक रनटाइम अपवाद की कीमत पर, वैकल्पिक के कम अलिखित को चुना। और एक क्रैश एक तर्क त्रुटि के लिए बेहतर है, जिसकी वजह से nilआप कहीं अपने डेटा में उम्मीद नहीं करते थे।

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

इसके बजाय, आप एक वैकल्पिक लौटने के लिए उप-वर्ग Arrayऔर ओवरराइड subscriptकर सकते हैं। या, अधिक व्यावहारिक रूप से, आप Arrayएक गैर-सबस्क्रिप्ट विधि के साथ विस्तार कर सकते हैं जो ऐसा करता है।

extension Array {

    // Safely lookup an index that might be out of bounds,
    // returning nil if it does not exist
    func get(index: Int) -> T? {
        if 0 <= index && index < count {
            return self[index]
        } else {
            return nil
        }
    }
}

var fruits: [String] = ["Apple", "Banana", "Coconut"]
if let fruit = fruits.get(1) {
    print("I ate a \( fruit )")
    // I ate a Banana
}

if let fruit = fruits.get(3) {
    print("I ate a \( fruit )")
    // never runs, get returned nil
}

स्विफ्ट 3 अपडेट

func get(index: Int) ->T? द्वारा प्रतिस्थापित किया जाना चाहिए func get(index: Int) ->Element?


2
+1 (और स्वीकार करें) subscript()एक वैकल्पिक के रिटर्न प्रकार को बदलने के साथ समस्या का उल्लेख करने के लिए - यह डिफ़ॉल्ट व्यवहार को ओवरराइड करने में सामना करना पड़ा प्राथमिक मार्ग था। (मैं वास्तव में इसे काम करने के लिए नहीं मिला ) मैं एक get()विस्तार विधि लिखने से बच रहा था , जो अन्य परिदृश्यों में स्पष्ट पसंद है (ओबज-सी श्रेणियों, किसी को भी?) लेकिन इससे get(बहुत बड़ा नहीं है [, और इसे बनाता है। स्पष्ट करें कि स्विफ्ट सबस्क्रिप्ट ऑपरेटर के बाहर अन्य डेवलपर्स क्या अपेक्षा कर सकते हैं, यह व्यवहार भिन्न हो सकता है। धन्यवाद!
क्रेग ओटिस

3
इसे और भी छोटा बनाने के लिए, मैं ();
हाइउउ

7
स्विफ्ट 2.0 Tका नाम बदल दिया गया है Element। बस एक मित्रतापूर्ण अनुस्मारक :)
Stas ज़ुकोवसकीय

3
इस चर्चा को जोड़ने के लिए, एक और कारण कि स्विफ्ट को एक वैकल्पिक वापस करने के लिए स्विफ्ट में बेक नहीं किया गया है, क्योंकि nilएक आउट-ऑफ-बाउंड्स इंडेक्स से अपवाद पैदा करने के बजाय वापस आना अस्पष्ट होगा। चूंकि Array<String?>संग्रह के वैध सदस्य के रूप में नील भी वापस आ सकता है, आप उन दो मामलों के बीच अंतर करने में सक्षम नहीं होंगे। यदि आपके पास अपना स्वयं का संग्रह प्रकार है जिसे आप जानते हैं कि आप कभी भी nilमूल्य नहीं लौटा सकते हैं , उर्फ ​​यह आवेदन के लिए प्रासंगिक है, तो आप इस पोस्ट में दिए गए जवाब के अनुसार सुरक्षित सीमा की जाँच के लिए स्विफ्ट का विस्तार कर सकते हैं।
आरोन

खूबसूरती से काम करता है
kamyFC

20

निकिता कुकुश्किन के उत्तर पर निर्माण करने के लिए, कभी-कभी आपको सरणी अनुक्रमित के साथ-साथ उनसे पढ़ने के लिए सुरक्षित रूप से असाइन करने की आवश्यकता होती है, अर्थात

myArray[safe: badIndex] = newValue

तो यहाँ निकिता के जवाब (स्विफ्ट 3.2) का एक अपडेट है जो सुरक्षित: पैरामीटर नाम को जोड़कर, परिवर्तनशील सरणी अनुक्रमित को सुरक्षित रूप से लिखने की अनुमति देता है।

extension Collection {
    /// Returns the element at the specified index iff it is within bounds, otherwise nil.
    subscript(safe index: Index) -> Element? {
        return indices.contains(index) ? self[ index] : nil
    }
}

extension MutableCollection {
    subscript(safe index: Index) -> Element? {
        get {
            return indices.contains(index) ? self[ index] : nil
        }

        set(newValue) {
            if let newValue = newValue, indices.contains(index) {
                self[ index] = newValue
            }
        }
    }
}

2
बेहद कम जवाब! यह सही तरीका है।
रीड करें

14

स्विफ्ट 2 में मान्य

भले ही यह पहले से ही बहुत बार उत्तर दिया जा चुका है, मैं स्विफ्ट प्रोग्रामिंग का फैशन लाइन में एक उत्तर अधिक प्रस्तुत करना चाहता हूं, जो कि क्रस्टी के शब्दों में है: "थिंक protocolएस फर्स्ट"

• हम क्या करना चाहते हैं?
- किसी Arrayदिए गए इंडेक्स का केवल तभी प्राप्त करें जब वह सुरक्षित हो, और nilअन्यथा
• यह कार्यशीलता का आधार क्या होना चाहिए?
- Array subscriptआईएनजी
• इसे यह सुविधा कहां से मिलती है?
- इसकी परिभाषा struct Arrayमें Swiftमॉड्यूल यह है
• कुछ भी नहीं है अधिक सामान्य / सार?
- यह अपनाता है protocol CollectionTypeजो इसे सुनिश्चित करता है
• कुछ अधिक सामान्य / सार नहीं?
- यह के protocol Indexableरूप में अच्छी तरह से गोद ले ...
• हाँ, सबसे अच्छा हम कर सकते हैं की तरह लगता है। क्या हम इसे अपनी इच्छानुसार इस सुविधा का विस्तार कर सकते हैं?
- लेकिन हमारे पास बहुत सीमित प्रकार (नहीं Int) और गुण हैं (नहीं)count) अब साथ काम करने के लिए!
• यह पर्याप्त होगा। स्विफ्ट की स्टडीलिब को बहुत अच्छी तरह से किया गया है;)

extension Indexable {
    public subscript(safe safeIndex: Index) -> _Element? {
        return safeIndex.distanceTo(endIndex) > 0 ? self[safeIndex] : nil
    }
}

,: सच नहीं है, लेकिन यह विचार देता है


2
एक स्विफ्ट नौसिखिया के रूप में मैं इस जवाब को नहीं समझता। अंत में कोड क्या दर्शाता है? क्या यह एक समाधान है, और यदि हां, तो मैं वास्तव में इसका उपयोग कैसे करूं?
थॉमस टेंपेलमैन

3
क्षमा करें, यह उत्तर अब स्विफ्ट 3 के लिए मान्य नहीं है, लेकिन प्रक्रिया निश्चित रूप से है। अंतर केवल इतना है कि अब आपको Collectionशायद :) पर रोक देना चाहिए
DeFrenZ

11
extension Array {
    subscript (safe index: Index) -> Element? {
        return 0 <= index && index < count ? self[index] : nil
    }
}
  • O (1) प्रदर्शन
  • सुरक्षित लिखें
  • [MyType?] के लिए वैकल्पिक रूप से सही तरीके से व्यवहार करता है (MyType ??, जो दोनों स्तरों पर अलिखित हो सकता है)
  • सेट्स के लिए समस्याएं पैदा नहीं करता है
  • संक्षिप्त कोड

यहाँ कुछ परीक्षण मैंने आपके लिए चलाए हैं:

let itms: [Int?] = [0, nil]
let a = itms[safe: 0] // 0 : Int??
a ?? 5 // 0 : Int?
let b = itms[safe: 1] // nil : Int??
b ?? 5 // nil : Int?
let c = itms[safe: 2] // nil : Int??
c ?? 5 // 5 : Int?

10
  • चूँकि सरणियाँ शून्य मानों को संचित कर सकती हैं, एक सरणी [सूचकांक] कॉल सीमा से बाहर होने पर शून्य वापस करने का कोई मतलब नहीं है।
  • क्योंकि हम नहीं जानते कि कोई उपयोगकर्ता सीमा समस्याओं से कैसे निपटना चाहता है, इसलिए कस्टम ऑपरेटरों का उपयोग करने का कोई मतलब नहीं है।
  • इसके विपरीत, अलिखित वस्तुओं के लिए पारंपरिक नियंत्रण प्रवाह का उपयोग करें और प्रकार की सुरक्षा सुनिश्चित करें।

यदि इंडेक्स = array.checkIndexForSafety (इंडेक्स: इंट)

  let item = array[safeIndex: index] 

यदि इंडेक्स = array.checkIndexForSafety (इंडेक्स: इंट)

  array[safeIndex: safeIndex] = myObject
extension Array {

    @warn_unused_result public func checkIndexForSafety(index: Int) -> SafeIndex? {

        if indices.contains(index) {

            // wrap index number in object, so can ensure type safety
            return SafeIndex(indexNumber: index)

        } else {
            return nil
        }
    }

    subscript(index:SafeIndex) -> Element {

        get {
            return self[index.indexNumber]
        }

        set {
            self[index.indexNumber] = newValue
        }
    }

    // second version of same subscript, but with different method signature, allowing user to highlight using safe index
    subscript(safeIndex index:SafeIndex) -> Element {

        get {
            return self[index.indexNumber]
        }

        set {
            self[index.indexNumber] = newValue
        }
    }

}

public class SafeIndex {

    var indexNumber:Int

    init(indexNumber:Int){
        self.indexNumber = indexNumber
    }
}

1
दिलचस्प दृष्टिकोण। कोई भी कारण SafeIndexएक वर्ग है और एक संरचना नहीं है?
स्टेफ

8

स्विफ्ट 4

अधिक पारंपरिक वाक्य रचना पसंद करने वालों के लिए एक एक्सटेंशन:

extension Array {

    func item(at index: Int) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

यदि सूचकांकों में इसका सूचकांक शामिल है, तो आपको यह जांचने के लिए सरणी तत्वों को विवश करने की आवश्यकता नहीं है।
लियो डबस

हां - अच्छा बिंदु - इसे केवल अतिरिक्त सुरक्षित तरीकों जैसे डिलीटऑब्जेक्ट, आदि के लिए आवश्यक होगा
Matjan

5

मैंने पाया कि सुरक्षित ऐरे मिलते हैं, सेट होते हैं, सम्मिलित होते हैं, बहुत उपयोगी होते हैं। मैं त्रुटियों को लॉग करना और अनदेखा करना पसंद करता हूं क्योंकि बाकी सभी को जल्द ही प्रबंधित करना मुश्किल हो जाता है। पूर्ण कोड बलो

/**
 Safe array get, set, insert and delete.
 All action that would cause an error are ignored.
 */
extension Array {

    /**
     Removes element at index.
     Action that would cause an error are ignored.
     */
    mutating func remove(safeAt index: Index) {
        guard index >= 0 && index < count else {
            print("Index out of bounds while deleting item at index \(index) in \(self). This action is ignored.")
            return
        }

        remove(at: index)
    }

    /**
     Inserts element at index.
     Action that would cause an error are ignored.
     */
    mutating func insert(_ element: Element, safeAt index: Index) {
        guard index >= 0 && index <= count else {
            print("Index out of bounds while inserting item at index \(index) in \(self). This action is ignored")
            return
        }

        insert(element, at: index)
    }

    /**
     Safe get set subscript.
     Action that would cause an error are ignored.
     */
    subscript (safe index: Index) -> Element? {
        get {
            return indices.contains(index) ? self[index] : nil
        }
        set {
            remove(safeAt: index)

            if let element = newValue {
                insert(element, safeAt: index)
            }
        }
    }
}

टेस्ट

import XCTest

class SafeArrayTest: XCTestCase {
    func testRemove_Successful() {
        var array = [1, 2, 3]

        array.remove(safeAt: 1)

        XCTAssert(array == [1, 3])
    }

    func testRemove_Failure() {
        var array = [1, 2, 3]

        array.remove(safeAt: 3)

        XCTAssert(array == [1, 2, 3])
    }

    func testInsert_Successful() {
        var array = [1, 2, 3]

        array.insert(4, safeAt: 1)

        XCTAssert(array == [1, 4, 2, 3])
    }

    func testInsert_Successful_AtEnd() {
        var array = [1, 2, 3]

        array.insert(4, safeAt: 3)

        XCTAssert(array == [1, 2, 3, 4])
    }

    func testInsert_Failure() {
        var array = [1, 2, 3]

        array.insert(4, safeAt: 5)

        XCTAssert(array == [1, 2, 3])
    }

    func testGet_Successful() {
        var array = [1, 2, 3]

        let element = array[safe: 1]

        XCTAssert(element == 2)
    }

    func testGet_Failure() {
        var array = [1, 2, 3]

        let element = array[safe: 4]

        XCTAssert(element == nil)
    }

    func testSet_Successful() {
        var array = [1, 2, 3]

        array[safe: 1] = 4

        XCTAssert(array == [1, 4, 3])
    }

    func testSet_Successful_AtEnd() {
        var array = [1, 2, 3]

        array[safe: 3] = 4

        XCTAssert(array == [1, 2, 3, 4])
    }

    func testSet_Failure() {
        var array = [1, 2, 3]

        array[safe: 4] = 4

        XCTAssert(array == [1, 2, 3])
    }
}

3
extension Array {
  subscript (safe index: UInt) -> Element? {
    return Int(index) < count ? self[Int(index)] : nil
  }
}

यदि उपर्युक्त विस्तार रिटर्न एनआईएल का उपयोग करना है तो कभी भी सूचकांक बाध्य हो जाता है।

let fruits = ["apple","banana"]
print("result-\(fruits[safe : 2])")

परिणाम - नील


3

मुझे एहसास है कि यह एक पुराना सवाल है। मैं इस बिंदु पर Swift5.1 का उपयोग कर रहा हूं, ओपी स्विफ्ट 1 या 2 के लिए था?

मुझे आज कुछ इस तरह की आवश्यकता थी, लेकिन मैं सिर्फ एक स्थान के लिए पूर्ण पैमाने पर विस्तार नहीं जोड़ना चाहता था और कुछ और अधिक कार्यात्मक (अधिक थ्रेड सुरक्षित;) चाहता था। मुझे भी नकारात्मक सूचकांकों से बचाने की जरूरत नहीं थी, बस वे जो किसी सरणी के अंत में हो सकते हैं:

let fruit = ["Apple", "Banana", "Coconut"]

let a = fruit.dropFirst(2).first // -> "Coconut"
let b = fruit.dropFirst(0).first // -> "Apple"
let c = fruit.dropFirst(10).first // -> nil

नील के साथ दृश्यों के बारे में बहस करने वालों के लिए, खाली संग्रह के लिए शून्य वापस करने वाले गुणों firstऔर lastगुणों के बारे में आप क्या करते हैं ?

मुझे यह पसंद आया क्योंकि मैं केवल मौजूदा सामान को पकड़ सकता था और इसका उपयोग वह परिणाम प्राप्त करने के लिए कर सकता था जो मुझे चाहिए था। मुझे यह भी पता है कि dropFirst (n) संपूर्ण संग्रह प्रतिलिपि नहीं है, सिर्फ एक टुकड़ा है। और फिर पहले से ही पहले से मौजूद व्यवहार मेरे लिए संभाल लेता है।


1

मुझे लगता है कि यह अच्छा विचार नहीं है। यह ठोस कोड बनाने के लिए बेहतर लगता है जिसके परिणामस्वरूप आउट-ऑफ-बाउंड इंडेक्स लागू करने की कोशिश नहीं होती है।

कृपया विचार करें कि ऐसी त्रुटि होने पर चुपचाप विफल रहें (जैसा कि ऊपर आपके कोड द्वारा सुझाया गया है) nil, और भी अधिक जटिल, अधिक अट्रैक्टिव एरर पैदा करने का खतरा है।

आप अपने ओवरराइड उसी तरह से कर सकते हैं जैसे आप इस्तेमाल करते हैं और सिर्फ अपने तरीके से सब्सक्राइब लिखते हैं। केवल दोष यह है कि मौजूदा कोड संगत नहीं होगा। मुझे लगता है कि जेनेरिक x [i] को ओवरराइड करने के लिए एक हुक खोजने के लिए (सी के रूप में एक पाठ प्रीप्रोसेसर के बिना भी) चुनौतीपूर्ण होगा।

निकटतम मैं सोच सकता हूं

// compile error:
if theIndex < str.count && let existing = str[theIndex]

संपादित करें : यह वास्तव में काम करता है। एक लाइन!!

func ifInBounds(array: [AnyObject], idx: Int) -> AnyObject? {
    return idx < array.count ? array[idx] : nil
}

if let x: AnyObject = ifInBounds(swiftarray, 3) {
    println(x)
}
else {
    println("Out of bounds")
}

6
मैं असहमत होगा - वैकल्पिक बंधन का मुद्दा यह है कि यह केवल तभी सफल होता है जब शर्त पूरी हो जाती है। (एक वैकल्पिक के लिए, इसका मतलब है कि एक मूल्य है।) if letइस मामले में एक का उपयोग करने से कार्यक्रम अधिक जटिल नहीं होता है, और न ही त्रुटियां अधिक विचलित होती हैं। यह केवल पारंपरिक दो-कथन ifसीमा की जांच करता है और एक-पंक्तिबद्ध, गाढ़ा बयान में वास्तविक खोज करता है। ऐसे मामले हैं (विशेष रूप से यूआई में काम कर रहे हैं) जहां एक सूचकांक के लिए सीमा से बाहर होना सामान्य है, जैसे चयन NSTableViewके selectedRowबिना मांग करना ।
क्रेग ओटिस

3
@ ओपी के सवाल के जवाब के बजाय, यह एक टिप्पणी है।
jlehr

1
@CraigOtis यकीन नहीं होता कि मैं सहमत हूँ। आप इस चेक को एक "सिंगल-लाइन, कंडेंस्ड स्टेटमेंट" में सक्सेसफुल तरीके से लिख सकते हैं , जैसे countElementsकि ओपी ने countजिस तरह से किया है , ठीक उसी तरह से जिस तरह से लैंग्वेज एरे सब्सक्रिप्शन को परिभाषित करता है।
मुंडी

1
@ जेलेहर शायद नहीं। किसी समस्या के इरादे या ज्ञान पर सवाल उठाना उचित खेल है।
मुंडी

2
@ मुंडी हेह, खासकर यदि आप बाद में इसे संपादित करते हैं तो वास्तव में इस सवाल का जवाब मिलेगा। :-)
जेलेहर

1

मैंने nilअपने उपयोग के मामले में एस के साथ सरणी गद्देदार की है :

let components = [1, 2]
var nilComponents = components.map { $0 as Int? }
nilComponents += [nil, nil, nil]

switch (nilComponents[0], nilComponents[1], nilComponents[2]) {
case (_, _, .Some(5)):
    // process last component with 5
default:
    break
}

safe:एरिका सदुन / माइक ऐश: http://ericasadun.com/2015/06/01/swift-safe-array-indexing-my-favorite-thing-of-the-new-week/ द्वारा लेबल के साथ सबस्क्रिप्ट एक्सटेंशन की जांच करें


0

स्विफ्ट सूची के लिए "सामान्य रूप से अस्वीकृत परिवर्तन" में क्रैश होने के बजाय वैकल्पिक वापसी के लिए ऐरे सबस्क्रिप्ट एक्सेस को बदलने का उल्लेख है :

बनाओ Array<T>सबस्क्रिप्ट पहुँच वापसी T?या T!बजाय T: वर्तमान सरणी व्यवहार है जानबूझकर , के रूप में यह सही ढंग से तथ्य यह है कि बाहर के सीमा सरणी का उपयोग एक तर्क त्रुटि है दर्शाता है। वर्तमान व्यवहार को बदलने से Arrayअस्वीकार्य डिग्री तक पहुंच बढ़ जाएगी । यह विषय पहले भी कई बार सामने आया है लेकिन इसे स्वीकार किए जाने की संभावना नहीं है।

https://github.com/apple/swift-evolution/blob/master/commonly_proposed.md#strings-characters-and-collection-types

तो एक वैकल्पिक वापसी के लिए मूल सबस्क्रिप्ट एक्सेस नहीं बदल रहा होगा।

हालाँकि, स्विफ्ट टीम / समुदाय एक नए वैकल्पिक-रिटर्न एक्सेस पैटर्न को जोड़ने के लिए खुला प्रतीत होता है , या तो फ़ंक्शन या सबस्क्रिप्ट के माध्यम से।

यह यहां स्विफ्ट इवोल्यूशन फोरम पर प्रस्तावित और चर्चा की गई है:

https://forums.swift.org/t/add-accessor-with-bounds-check-to-array/16871

विशेष रूप से, क्रिस लैटनर ने विचार को "+1" दिया:

सहमत, इसके लिए सबसे अधिक सुझाई जाने वाली वर्तनी है: yourArray[safe: idx]जो मुझे बहुत अच्छी लगती है। इसे जोड़ने के लिए मैं बहुत +1 हूं।

https://forums.swift.org/t/add-accessor-with-bounds-check-to-array/16871/13

तो यह स्विफ्ट के कुछ भविष्य के संस्करण में बॉक्स से बाहर हो सकता है। मैं किसी को भी प्रोत्साहित करता हूं जो चाहता है कि वह उस स्विफ्ट इवोल्यूशन थ्रेड में योगदान करे।


0

यह प्रचारित करने के लिए कि ऑपरेशन विफल क्यों होते हैं, त्रुटियाँ वैकल्पिक से बेहतर होती हैं। सदस्यता त्रुटियों को नहीं फेंक सकती है, इसलिए यह एक विधि है।

public extension Collection {
  /// - Returns: same as subscript, if index is in bounds
  /// - Throws: CollectionIndexingError
  func element(at index: Index) throws -> Element {
    guard indices.contains(index)
    else { throw CollectionIndexingError() }

    return self[index]
  }
}

/// Thrown when `element(at:)` is called with an invalid index.
public struct CollectionIndexingError: Error { }
XCTAssertThrowsError( try ["🐾", "🥝"].element(at: 2) )

let optionals = [1, 2, nil]
XCTAssertEqual(try optionals.element(at: 0), 1)

XCTAssertThrowsError( try optionals.element(at: optionals.endIndex) )
{ XCTAssert($0 is CollectionIndexingError) }

0

निश्चित नहीं है कि किसी ने भी, ऐसा एक्सटेंशन क्यों नहीं रखा है जिसमें सरणी को स्वचालित रूप से बढ़ने के लिए एक सेटर भी है

extension Array where Element: ExpressibleByNilLiteral {
    public subscript(safe index: Int) -> Element? {
        get {
            guard index >= 0, index < endIndex else {
                return nil
            }

            return self[index]
        }

        set(newValue) {
            if index >= endIndex {
                self.append(contentsOf: Array(repeating: nil, count: index - endIndex + 1))
            }

            self[index] = newValue ?? nil
        }
    }
}

उपयोग आसान है और स्विफ्ट 5.1 के रूप में काम करता है

var arr:[String?] = ["A","B","C"]

print(arr) // Output: [Optional("A"), Optional("B"), Optional("C")]

arr[safe:10] = "Z"

print(arr) // [Optional("A"), Optional("B"), Optional("C"), nil, nil, nil, nil, nil, nil, nil, Optional("Z")]

नोट: आपको प्रदर्शन की लागत (समय / स्थान दोनों में) को समझना चाहिए, जब तेजी से एक सरणी बढ़ रही है - लेकिन छोटी समस्याओं के लिए कभी-कभी आपको केवल स्विफ्ट को रोकने के लिए पैर में स्वयं को स्थानांतरित करने की आवश्यकता होती है


-1

मैंने सरणी के लिए एक सरल विस्तार किया है

extension Array where Iterator.Element : AnyObject {
    func iof (_ i : Int ) -> Iterator.Element? {
        if self.count > i {
            return self[i] as Iterator.Element
        }
        else {
            return nil
        }
    }

}

यह पूरी तरह से डिजाइन के अनुसार काम करता है

उदाहरण

   if let firstElemntToLoad = roots.iof(0)?.children?.iof(0)?.cNode, 

-1

स्विफ्ट 5 उपयोग

extension WKNavigationType {
    var name : String {
        get {
            let names = ["linkAct","formSubm","backForw","reload","formRelo"]
            return names.indices.contains(self.rawValue) ? names[self.rawValue] : "other"
        }
    }
}

के साथ समाप्त हो गया लेकिन वास्तव में आम तौर पर करना चाहता था

[<collection>][<index>] ?? <default>

लेकिन जैसा कि संग्रह प्रासंगिक है मुझे लगता है कि यह उचित है।


यह उत्तर स्वीकृत स्वीकार से अलग कैसे है? मेरे लिए, यह बिल्कुल (डुप्लिकेट) समान दिखता है।
लेगोनफैक्टिक

-1

जब आपको केवल एक सरणी से मान प्राप्त करने की आवश्यकता होती है और आपको एक छोटे प्रदर्शन पर जुर्माना नहीं लगता है (यानी यदि आपका संग्रह बहुत बड़ा नहीं है), तो एक शब्दकोश-आधारित विकल्प है जिसमें शामिल नहीं है (मेरे लिए भी बहुत सामान्य)। स्वाद) संग्रह विस्तार:

// Assuming you have a collection named array:
let safeArray = Dictionary(uniqueKeysWithValues: zip(0..., array))
let value = safeArray[index] ?? defaultValue;
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.