Безпечний пошук масиву в Swift через необов'язкові прив’язки?


271

Якщо у мене є масив у Swift, і я намагаюся отримати доступ до індексу, який знаходиться поза межами, виникає не дивна помилка виконання:

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оператор ol ', щоб перевірити і переконатися, що індекс менший за str.count.

Я спробував додати власну subscript()реалізацію, але я не впевнений, як передати дзвінок до оригінальної реалізації чи отримати доступ до елементів (на основі індексу), не використовуючи позначення підписки:

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

2
Я усвідомлюю, що це трохи OT, але я також вважаю, що було б добре, якби у Свіфта був чіткий синтаксис для виконання будь-якої перевірки меж, включаючи списки. У нас уже є відповідне ключове слово для цього, наприклад. Так, наприклад, якщо X в (1,2,7) ... або якщо X в myArray
Maury Markowitz

Відповіді:


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
    }
}

Swift 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
    }
}

Подяку Хамішу за те, що він розробив рішення для Swift 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
Станом на Swift 2 (Xcode 7) для цього потрібно трохи підправити:return self.indices ~= index ? self[index] : nil;
Тим

7
Що стосується версії Swift 3: можливо наріжний випадок тільки-запрошення, але підказка , тим не менше: є випадки , коли «безпечна» індекс версія вище не є безпечною ( в той час як версія Swift 2 була): для Collectionтипів , де Indicesзнаходяться не суміжний. Наприклад Set, якщо ми маємо доступ до заданого елемента за допомогою індексу ( SetIndex<Element>), ми можемо зіткнутися з винятками для виконання індексів, які є, >= startIndexі < endIndexв цьому випадку безпечний підписник не вдається (див., Наприклад, цей надуманий приклад ).
dfri

12
УВАГА! Перевірка масивів таким чином може бути дуже дорогою. containsМетод ітерації за всіма показниками , таким чином , роблячи це O (N). Кращий спосіб - використовувати індекс і рахувати для перевірки меж.
Стефан Василевич

6
Щоб запобігти генеруванню індексів та ітерації над ними (O (n)), краще використовувати порівняння (O (1)): return index >= startIndex && index < endIndex ? self[index] : nil Collectionтипи мають startIndex, endIndexякі є Comparable. Звичайно, це не спрацює для деяких дивних колекцій, у яких, наприклад, немає індексів посередині, рішення з indicesбільш загальним.
зубко

57

Якщо ви дійсно хочете такої поведінки, це пахне так, як ви хочете Словник замість масиву. Словники повертаються nilпід час доступу до відсутніх ключів, що має сенс, оскільки набагато складніше знати, чи ключ присутній у словнику, оскільки ці ключі можуть бути чим завгодно, де в масиві ключ повинен знаходитись у діапазоні: 0до count. І це надзвичайно звичайно для ітерації в цьому діапазоні, де ви можете бути абсолютно впевнені, що маєте реальне значення для кожної ітерації циклу.

Я думаю, що причиною цього не працює такий вибір дизайну, який зробили розробники Swift. Візьміть свій приклад:

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

Якщо ви вже знаєте, що індекс існує, як це робиться в більшості випадків, коли ви використовуєте масив, цей код чудовий. Однак, якщо доступ до підрядкові міг повернутися , nilто ви змінили тип повертається з Array«S subscriptспособу бути необов'язковим. Це змінює ваш код на:

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

Що означає, що вам потрібно буде розгортати необов'язковий кожен раз, коли ви перебираєте масив чи щось інше з відомим індексом, лише тому, що рідко ви можете отримати доступ до індексу поза межами меж. Дизайнери Swift вибрали менше розгортання додаткових опцій за рахунок виключення під час виконання, коли отримували доступ до індексів за межами меж. І крах є кращим, ніж логічна помилка, викликана ти, де 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()метод розширення, що є очевидним вибором в інших сценаріях (категорії Obj-C, хтось?), Але get(не набагато більший за це [, і робить це Зрозуміло, що поведінка може відрізнятися від того, що інші розробники можуть очікувати від оператора підписки Swift. Дякую!
Крейг Отіс

3
Щоб зробити його ще коротшим, я використовую на ();) Спасибі!
hyouuu

7
Станом на Swift 2.0 Tбуло перейменовано на Element. Просто привітне нагадування :)
Стас Жуковський

3
Щоб додати до цього обговорення, ще одна причина, чому перевірка меж не закладається у Swift для повернення необов’язкового, полягає в тому, що повернення nilзамість викликання виключення з індексу поза межами було б неоднозначним. Оскільки, наприклад, Array<String?>можна також повернути нуль як дійсний член колекції, ви не зможете розмежувати ці два випадки. Якщо у вас є власний тип колекції, який, як ви знаєте, ніколи не може повернути nilзначення, але це контекстуально для програми, тоді ви можете продовжити Swift для перевірки безпечних меж, як відповіли в цьому дописі.
Аарон

Працює прекрасно
kamyFC

20

Щоб спиратися на відповідь Микити Кукушкіна, іноді потрібно безпечно призначати індекси масиву, а також читати з них, тобто

myArray[safe: badIndex] = newValue

Отож ось оновлення відповіді Нікіти (Swift 3.2), яке також дозволяє безпечно записувати до змінних індексів масиву, додаючи ім’я параметра safe:.

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

Дійсний у Swift 2

Незважаючи на те, що на це вже було відповідено багато разів, я хотів би представити відповідь детальніше в тому напрямку, де йде мода програмування Swift, яка, за словами Крісті, є: "Подумайте protocolспочатку"

• Що ми хочемо зробити?
- Отримуйте елемент Arrayданого індексу лише тоді, коли це безпечно, nilінакше
• На чому повинна базуватися ця функціональна реалізація?
- Array subscripting
• Звідки вона бере цю функцію?
- Його визначення 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
На жаль, ця відповідь більше не діє для Swift 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
  • Оскільки масиви можуть зберігати нульові значення, не має сенсу повертати нуль, якщо виклик масиву [індекс] знаходиться поза межами.
  • Оскільки ми не знаємо, як користувач хотів би вирішити проблеми, що не входять у межі, користуватися спеціальними операторами не має сенсу.
  • На відміну від цього, використовуйте традиційний керуючий потік для розгортання об'єктів та забезпечте безпеку типу.

якщо дозволити index = array.checkIndexForSafety (індекс: Int)

  let item = array[safeIndex: index] 

якщо дозволити index = array.checkIndexForSafety (індекс: Int)

  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- це клас, а не структура?
stef

8

Швидкий 4

Розширення для тих, хто віддає перевагу більш традиційний синтаксис:

extension Array {

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

вам не потрібно обмежувати елементи масиву рівними, щоб перевірити, чи містить індекси його індекс.
Лев Дабус

да - хороша точка - це було б необхідно тільки для додаткових безпечних методів , таких як DeleteObject і т.д.
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 на даний момент, ОП був для Swift 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]

EDIT : Це фактично працює. Однолінійний !!

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
@Mundi, здається, це коментар, а не відповідь на питання ОП.
jlehr

1
@CraigOtis Не впевнений, що я згоден. Ви можете написати цей чек лаконічно в "однорядковому, стислому заяві", наприклад, використовуючи countElementsабо як це робив ОП count, тільки не так, як мова визначає написання підписок масиву.
Mundi

1
@jlehr Можливо, ні. Це справедлива гра, щоб поставити під сумнів наміри чи розумність поставленої проблеми.
Mundi

2
@Mundi Heh, особливо якщо згодом ти його відредагуєш, щоб насправді відповісти на питання. :-)
jlehr

1

Я застосував масив nils з у моєму випадку використання:

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

У списку Swift "Загальновідхилені зміни" міститься згадка про зміну доступу до підпису Array, щоб повернути необов'язковий, а не збій:

Зробити Array<T>повернення доступу до підрозділу T?або T!замість цього T: Поточна поведінка масиву навмисна , оскільки вона точно відображає той факт, що доступ до масиву поза межами є логічною помилкою. Зміна поточної поведінки призведе до уповільнення Arrayдоступу до неприйнятного ступеня. Ця тема раніше виходила кілька разів, але навряд чи вона буде прийнята.

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

Таким чином, базовий доступ до підписки не змінюватиметься для повернення необов’язкового.

Однак команда / спільнота Swift здається відкритою для додавання нової необов'язково повертається схеми доступу до масивів, або через функцію, або з підписки.

Це було запропоновано та обговорено на форумі Swift Evolution тут:

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

Тож це може виявитися неможливим у майбутній версії Swift. Я б закликав усіх, хто хоче, щоб він сприяв цій темі Swift Evolution.


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
        }
    }
}

Використання легко та працює як у Swift 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")]

Примітка. Ви повинні розуміти вартість продуктивності (як у часі, так і в просторі), коли швидко зростає масив - але для невеликих проблем іноді просто потрібно змусити Swift зупинити Swifting в ногу


-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 та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.