Tra cứu mảng an toàn (giới hạn kiểm tra) trong Swift, thông qua các ràng buộc tùy chọn?


271

Nếu tôi có một mảng trong Swift và cố gắng truy cập vào một chỉ mục nằm ngoài giới hạn, có một lỗi thời gian chạy không đáng ngạc nhiên:

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

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

Tuy nhiên, tôi đã nghĩ với tất cả các chuỗi tùy chọn và sự an toàn mà Swift mang lại, sẽ thật tầm thường khi làm một việc như:

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

Thay vì:

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

Nhưng đây không phải là trường hợp - tôi phải sử dụng ifcâu lệnh ol ' để kiểm tra và đảm bảo chỉ số nhỏ hơn str.count.

Tôi đã thử thêm subscript()triển khai của riêng mình , nhưng tôi không chắc chắn làm thế nào để chuyển cuộc gọi đến triển khai ban đầu hoặc truy cập vào các mục (dựa trên chỉ mục) mà không sử dụng ký hiệu đăng ký:

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

2
Tôi nhận ra đây là một chút OT, nhưng tôi cũng cảm thấy thật tuyệt nếu Swift có cú pháp rõ ràng để thực hiện bất kỳ loại kiểm tra giới hạn nào, bao gồm cả danh sách. Chúng tôi đã có một từ khóa phù hợp cho việc này, ví dụ: nếu X trong (1,2,7) ... hoặc nếu X trong myArray
Maury Markowitz

Câu trả lời:


652

Câu trả lời của Alex có lời khuyên và giải pháp tốt cho câu hỏi, tuy nhiên, tôi đã tình cờ tìm thấy một cách tốt hơn để thực hiện chức năng này:

Swift 3.2 trở lên

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

Tín dụng cho Hamish để đưa ra giải pháp cho Swift 3 .

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

Thí dụ

let array = [1, 2, 3]

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

45
Tôi nghĩ rằng điều này chắc chắn xứng đáng được chú ý - công việc tốt đẹp. Tôi thích safe:tên tham số bao gồm để đảm bảo sự khác biệt.
Craig Otis

11
Kể từ Swift 2 (Xcode 7), điều này cần một chút tinh chỉnh:return self.indices ~= index ? self[index] : nil;
Tim

7
Về phiên bản Swift 3: có thể là một góc hợp cụ thể chỉ nhắc, nhưng một dấu nhắc dù sao: có trường hợp "an toàn" phiên bản subscript trên là không an toàn (trong khi phiên bản Swift 2 là): cho Collectionloại nơi Indiceslà không tiếp giáp Ví dụ: đối với các Settrường hợp, nếu chúng ta truy cập vào một phần tử được thiết lập bởi index ( SetIndex<Element>), chúng ta có thể chạy vào các ngoại lệ thời gian chạy cho các chỉ mục >= startIndex< endIndex, trong trường hợp đó, chỉ số an toàn không thành công (xem ví dụ này có thể xảy ra ).
dfri

12
CẢNH BÁO! Kiểm tra mảng theo cách này có thể thực sự tốn kém. Các containsphương pháp sẽ lặp qua tất cả các chỉ số do đó làm cho này một O (n). Một cách tốt hơn là sử dụng chỉ số và đếm để kiểm tra giới hạn.
Stefan Vasiljevic

6
Để ngăn việc tạo các chỉ mục và lặp lại trên chúng (O (n)), tốt hơn là sử dụng so sánh (O (1)): return index >= startIndex && index < endIndex ? self[index] : nil Collectioncác loại có startIndex, endIndexđó là Comparable. Tất nhiên, điều này sẽ không hoạt động đối với một số bộ sưu tập lạ, ví dụ, không có chỉ số ở giữa, giải pháp với indicestổng quát hơn.
zubko

57

Nếu bạn thực sự muốn hành vi này, nó có mùi giống như bạn muốn có Từ điển thay vì Mảng. Từ điển trở lại nilkhi truy cập các khóa bị thiếu, điều này có ý nghĩa bởi vì rất khó để biết liệu một khóa có trong từ điển hay không vì các khóa đó có thể là bất cứ thứ gì, trong đó một mảng phải nằm trong một phạm vi: 0to count. Và nó cực kỳ phổ biến để lặp lại trong phạm vi này, nơi bạn có thể hoàn toàn chắc chắn có một giá trị thực trên mỗi lần lặp của một vòng lặp.

Tôi nghĩ lý do nó không hoạt động theo cách này là một lựa chọn thiết kế được thực hiện bởi các nhà phát triển Swift. Lấy ví dụ của bạn:

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

Nếu bạn đã biết chỉ mục tồn tại, như bạn làm trong hầu hết các trường hợp bạn sử dụng một mảng, mã này là tuyệt vời. Tuy nhiên, nếu truy cập vào một subscript có thể có thể trở lại nilsau đó bạn đã thay đổi kiểu trả về của Array's subscriptphương pháp là một tùy chọn. Điều này thay đổi mã của bạn thành:

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

Điều đó có nghĩa là bạn sẽ cần mở ra một tùy chọn mỗi khi bạn lặp qua một mảng hoặc làm bất cứ điều gì khác với một chỉ mục đã biết, chỉ vì hiếm khi bạn có thể truy cập vào một chỉ mục ngoài giới hạn. Các nhà thiết kế Swift đã chọn cách ít mở khóa các tùy chọn hơn, với chi phí ngoại lệ thời gian chạy khi truy cập các chỉ số giới hạn. Và một sự cố tốt hơn là lỗi logic do nilbạn không mong đợi trong dữ liệu của mình ở đâu đó.

Và tôi đồng ý với họ. Vì vậy, bạn sẽ không thay đổi Arraytriển khai mặc định vì bạn sẽ phá vỡ tất cả mã mong đợi các giá trị không tùy chọn từ mảng.

Thay vào đó, bạn có thể phân lớp Arrayvà ghi đè subscriptđể trả về tùy chọn. Hoặc, thực tế hơn, bạn có thể mở rộng Arrayvới một phương pháp không đăng ký thực hiện điều này.

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
}

Cập nhật Swift 3

func get(index: Int) ->T? cần phải được thay thế bởi func get(index: Int) ->Element?


2
+1 (và chấp nhận) để đề cập đến vấn đề thay đổi loại trả về subscript()thành tùy chọn - đây là rào cản chính phải đối mặt trong việc ghi đè hành vi mặc định. (Tôi thực sự không thể làm cho nó hoạt động được . ) Tôi đã tránh viết một get()phương pháp khuyến nông, đó là sự lựa chọn rõ ràng trong các kịch bản khác (loại obj-C, bất cứ ai?) Nhưng get(không phải là lớn hơn nhiều so với [, và làm cho nó rõ ràng rằng hành vi có thể khác với những gì các nhà phát triển khác có thể mong đợi từ toán tử đăng ký Swift. Cảm ơn bạn!
Craig Otis

3
Để làm cho nó thậm chí ngắn hơn, tôi sử dụng tại ();) Cảm ơn!
hyouuu

7
Kể từ Swift 2.0 T đã được đổi tên thành Element. Chỉ là một lời nhắc nhở thân thiện :)
Stas Zhukovskiy

3
Để thêm vào cuộc thảo luận này, một lý do khác khiến việc kiểm tra giới hạn không được đưa vào Swift để trả lại tùy chọn là vì quay lại nilthay vì gây ra ngoại lệ từ chỉ số ngoài giới hạn sẽ không rõ ràng. Vì ví dụ Array<String?>cũng có thể trả về con số không là thành viên hợp lệ của bộ sưu tập, bạn sẽ không thể phân biệt giữa hai trường hợp đó. Nếu bạn có loại bộ sưu tập của riêng mình mà bạn biết không bao giờ có thể trả về nilgiá trị, hay còn gọi là bối cảnh cho ứng dụng, thì bạn có thể mở rộng Swift để kiểm tra giới hạn an toàn như đã trả lời trong bài đăng này.
Aaron

Hoạt động rất đẹp
kamyFC

20

Để xây dựng dựa trên câu trả lời của Nikita Kukushkin, đôi khi bạn cần gán an toàn cho các chỉ mục mảng cũng như đọc từ chúng, tức là

myArray[safe: badIndex] = newValue

Vì vậy, đây là bản cập nhật cho câu trả lời của Nikita (Swift 3.2) cũng cho phép ghi an toàn vào các chỉ mục mảng có thể thay đổi, bằng cách thêm tên tham số 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
Câu trả lời cực kỳ bị đánh giá thấp! Đây là cách chính xác tot làm điều này!
Reid

14

Hợp lệ trong Swift 2

Mặc dù điều này đã được trả lời rất nhiều lần rồi, tôi muốn trình bày câu trả lời nhiều hơn theo hướng thời trang của lập trình Swift đang diễn ra, theo cách nói của Crusty là: " protocolTrước tiên hãy nghĩ "

• Chúng ta muốn làm cái gì?
- Chỉ nhận được một phần tử của một ArrayChỉ số khi nó an toàn và nilnếu không
• Chức năng này nên dựa trên cơ sở nào?
- Array subscripting
• Nó lấy tính năng này từ đâu?
- Định nghĩa của nó struct Arraytrong Swiftmô-đun có nó
• Không có gì chung chung / trừu tượng hơn?
- Nó thông qua protocol CollectionTypemà đảm bảo nó là tốt
• Không có gì chung chung / trừu tượng?
- Nó cũng chấp nhận protocol Indexable...
• Yup, nghe có vẻ tốt nhất chúng ta có thể làm. Sau đó chúng tôi có thể mở rộng nó để có tính năng này mà chúng tôi muốn không?
- Nhưng chúng tôi có các loại rất hạn chế (không Int) và thuộc tính (khôngcount) để làm việc với bây giờ!
• Nó sẽ đủ thôi. Stdlib của Swift được thực hiện khá tốt;)

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

: Không đúng, nhưng nó cho ý tưởng


2
Là một người mới chơi Swift, tôi không hiểu câu trả lời này. Mã ở cuối đại diện cho cái gì? Đó có phải là một giải pháp không, và nếu vậy, làm thế nào để tôi thực sự sử dụng nó?
Thomas Tempelmann

3
Xin lỗi, câu trả lời này không còn hợp lệ nữa đối với Swift 3, nhưng quá trình chắc chắn là như vậy. Sự khác biệt duy nhất là bây giờ bạn nên dừng lại Collectioncó lẽ :)
DeFrenZ

11
extension Array {
    subscript (safe index: Index) -> Element? {
        return 0 <= index && index < count ? self[index] : nil
    }
}
  • Hiệu suất O (1)
  • loại an toàn
  • giao dịch chính xác với Tùy chọn cho [MyType?] (trả về MyType ??, có thể được mở ra ở cả hai cấp độ)
  • không dẫn đến sự cố cho Bộ
  • mã súc tích

Dưới đây là một số bài kiểm tra tôi đã chạy cho bạn:

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
  • Bởi vì các mảng có thể lưu trữ các giá trị nil, sẽ không có nghĩa là trả về một con số không nếu một lệnh gọi [index] nằm ngoài giới hạn.
  • Vì chúng tôi không biết người dùng muốn xử lý các vấn đề giới hạn như thế nào, nên việc sử dụng các toán tử tùy chỉnh sẽ không có ý nghĩa gì.
  • Ngược lại, sử dụng luồng điều khiển truyền thống cho các đối tượng mở gói và đảm bảo an toàn loại.

if let index = Array.check IndexForSafe (index: Int)

  let item = array[safeIndex: index] 

if let index = Array.check IndexForSafe (index: 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
Cách tiếp cận thú vị. Bất kỳ lý do SafeIndexlà một lớp học và không phải là một cấu trúc?
Stef

8

Swift 4

Một phần mở rộng cho những người thích một cú pháp truyền thống hơn:

extension Array {

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

bạn không cần ràng buộc các phần tử mảng để tương đương để kiểm tra xem các chỉ mục có chứa chỉ mục của nó không.
Leo Dabus

có - điểm tốt - chỉ cần cho các phương thức an toàn bổ sung như xóaObject, v.v.
Matjan

5

Tôi thấy mảng an toàn get, set, insert, remove rất hữu ích. Tôi thích đăng nhập và bỏ qua các lỗi vì tất cả những thứ khác sẽ sớm trở nên khó quản lý. Mã đầy đủ dưới đây

/**
 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)
            }
        }
    }
}

Xét nghiệm

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

Sử dụng Trên đề cập đến phần mở rộng trả về nil nếu chỉ số bất kỳ lúc nào vượt quá giới hạn.

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

kết quả - không


3

Tôi nhận ra đây là một câu hỏi cũ. Tôi đang sử dụng Swift5.1 tại thời điểm này, OP dành cho Swift 1 hoặc 2?

Tôi cần một cái gì đó như thế này ngày hôm nay, nhưng tôi không muốn thêm một phần mở rộng quy mô đầy đủ cho chỉ một nơi và muốn một cái gì đó có nhiều chức năng hơn (an toàn hơn cho chủ đề?). Tôi cũng không cần phải bảo vệ chống lại các chỉ số tiêu cực, chỉ những chỉ số có thể đã qua cuối mảng:

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

Đối với những người tranh luận về Chuỗi với số không, bạn sẽ làm gì với firstlast thuộc tính trả về số không cho các bộ sưu tập trống?

Tôi thích điều này bởi vì tôi có thể lấy những thứ hiện có và sử dụng nó để có được kết quả mà tôi muốn. Tôi cũng biết rằng dropFirst (n) không phải là một bản sao toàn bộ bộ sưu tập, chỉ là một lát cắt. Và sau đó, hành vi đã tồn tại đầu tiên chiếm lấy tôi.


1

Tôi nghĩ rằng đây không phải là một ý tưởng tốt. Có vẻ tốt hơn để xây dựng mã vững chắc mà không dẫn đến việc cố gắng áp dụng các chỉ mục ngoài giới hạn.

Vui lòng xem xét rằng có một lỗi như vậy thất bại âm thầm (như được đề xuất bởi mã của bạn ở trên) bằng cách quay lại nil có xu hướng tạo ra các lỗi thậm chí phức tạp hơn, khó hiểu hơn.

Bạn có thể thực hiện ghi đè của mình theo cách tương tự bạn đã sử dụng và chỉ cần viết các mục con theo cách riêng của bạn. Hạn chế duy nhất là mã hiện tại sẽ không tương thích. Tôi nghĩ rằng để tìm một cái móc để ghi đè x [i] chung (cũng không có bộ xử lý văn bản như trong C) sẽ là một thách thức.

Gần nhất tôi có thể nghĩ là

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

EDIT : Điều này thực sự hoạt động. Lót!!

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
Tôi sẽ không đồng ý - quan điểm của ràng buộc tùy chọn là nó chỉ thành công nếu điều kiện được đáp ứng. (Đối với một tùy chọn, điều đó có nghĩa là có một giá trị.) Sử dụng một if lettrong trường hợp này không làm cho chương trình phức tạp hơn, cũng không có lỗi khó hiểu hơn. Nó chỉ đơn giản là ngưng tụ ifkiểm tra giới hạn hai câu lệnh truyền thống và tra cứu thực tế thành một câu lệnh đơn, cô đọng. Có những trường hợp (đặc biệt là làm việc trong một giao diện người dùng), nơi nó là bình thường đối với một chỉ số để được ra khỏi giới hạn, như yêu cầu một NSTableViewcho selectedRowmà không có một lựa chọn.
Craig Otis

3
@Mundi đây dường như là một bình luận, thay vì một câu trả lời cho câu hỏi của OP.
jlehr

1
@CraigOtis Không chắc tôi đồng ý. Bạn có thể viết kiểm tra này một cách cô đọng trong một "câu lệnh đơn, cô đọng", ví dụ như sử dụng countElementshoặc như OP đã làm với count, không phải theo cách ngôn ngữ định nghĩa việc viết các mảng con.
Mundi

1
@jlehr Có lẽ không. Đây là trò chơi công bằng để đặt câu hỏi về ý định hoặc sự khôn ngoan của một vấn đề được đặt ra.
Mundi

2
@Mundi Heh, đặc biệt nếu sau này bạn chỉnh sửa nó để thực sự trả lời câu hỏi. :-)
jlehr

1

Tôi đã đệm mảng với nils trong trường hợp sử dụng của mình:

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
}

Đồng thời kiểm tra phần mở rộng đăng ký với safe:nhãn của Erica Sadun / Mike Ash: http://ericasadun.com/2015/06/01/swift-safe-array-indexing-my-favorite-thing-of-the-new-week/


0

Danh sách "Các thay đổi thường bị từ chối" cho danh sách Swift có đề cập đến việc thay đổi quyền truy cập đăng ký Mảng để trả về một tùy chọn thay vì sụp đổ:

Thực hiện Array<T>trả lại quyền truy cập đăng ký T?hoặc T!thay vì T: Hành vi mảng hiện tại là có chủ ý , vì nó phản ánh chính xác thực tế rằng truy cập mảng ngoài giới hạn là lỗi logic. Thay đổi hành vi hiện tại sẽ làm chậm Arraytruy cập đến một mức độ không thể chấp nhận. Chủ đề này đã đưa ra nhiều lần trước đây nhưng rất khó được chấp nhận.

https://github.com/apple/swift-evolution/blob/master/commonly_proposed.md#strings-char character-and-collection-type

Vì vậy, quyền truy cập đăng ký cơ bản sẽ không thay đổi để trả về một tùy chọn.

Tuy nhiên, nhóm / cộng đồng Swift dường như mở để thêm một mẫu truy cập trả về tùy chọn mới vào Mảng, thông qua chức năng hoặc đăng ký.

Điều này đã được đề xuất và thảo luận trên diễn đàn Swift Evolution tại đây:

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

Đáng chú ý, Chris Lattner đã đưa ra ý tưởng "+1":

Đồng ý, cách đánh vần được đề xuất thường xuyên nhất cho điều này là : yourArray[safe: idx], có vẻ tuyệt vời đối với tôi. Tôi rất +1 để thêm điều này.

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

Vì vậy, điều này có thể được đưa ra khỏi hộp trong một số phiên bản tương lai của Swift. Tôi khuyến khích bất cứ ai muốn nó đóng góp cho chủ đề Swift Evolution đó.


0

Để tuyên truyền tại sao các hoạt động thất bại, lỗi là tốt hơn so với tùy chọn. Đăng ký không thể ném lỗi, vì vậy nó phải là một phương pháp.

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

Không chắc chắn tại sao không có ai, đã đưa ra một tiện ích mở rộng cũng có trình cài đặt để tự động phát triển mảng

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

Việc sử dụng rất dễ dàng và hoạt động kể từ 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")]

Lưu ý: Bạn nên hiểu chi phí hiệu năng (cả về thời gian / không gian) khi phát triển một mảng nhanh chóng - nhưng đối với các vấn đề nhỏ đôi khi bạn chỉ cần khiến Swift ngừng tự dừng chân


-1

Tôi đã thực hiện một phần mở rộng đơn giản cho mảng

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

}

nó hoạt động hoàn hảo như thiết kế

Thí dụ

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

-1

Cách sử dụng Swift 5

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

kết thúc với nhưng thực sự muốn làm nói chung là thích

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

nhưng vì bộ sưu tập theo ngữ cảnh nên tôi đoán nó phù hợp.


Câu trả lời này khác với câu trả lời như thế nào? Đối với tôi, nó trông giống hệt nhau (trùng lặp).
Legonaftik

-1

Khi bạn chỉ cần nhận các giá trị từ một mảng và bạn không phải chịu một hình phạt hiệu suất nhỏ (nghĩa là nếu bộ sưu tập của bạn không lớn), có một sự thay thế dựa trên Từ điển không liên quan (quá chung chung, đối với tôi hương vị) bộ sưu tập mở rộng:

// Assuming you have a collection named array:
let safeArray = Dictionary(uniqueKeysWithValues: zip(0..., array))
let value = safeArray[index] ?? defaultValue;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.