Mảng hai chiều trong Swift


109

Tôi rất bối rối về mảng 2D trong Swift. Hãy để tôi mô tả từng bước. Và bạn vui lòng sửa cho tôi nếu tôi sai.

Đầu tiên; khai báo một mảng trống:

class test{
    var my2Darr = Int[][]()
}

Thứ hai điền vào mảng. (chẳng hạn như my2Darr[i][j] = 0trong đó i, j là các biến vòng lặp)

class test {
    var my2Darr = Int[][]()
    init() {
        for(var i:Int=0;i<10;i++) {
            for(var j:Int=0;j<10;j++) {
                my2Darr[i][j]=18   /*  Is this correct?  */
            }
        }
    }
}

Và cuối cùng, Chỉnh sửa phần tử trong mảng

class test {
    var my2Darr = Int[][]()
    init() {
        ....  //same as up code
    }
    func edit(number:Int,index:Int){
        my2Darr[index][index] = number
        // Is this correct? and What if index is bigger
        // than i or j... Can we control that like 
        if (my2Darr[i][j] == nil) { ...  }   */
    }
}

Bạn đang gặp vấn đề với cách tiếp cận của mình?
Alex Wayne

2
Mong bạn thông cảm, toàn bộ bước thứ hai của bạn có thể được giảm xuống mức này var my2DArray = Array(count: 10, repeatedValue: Array(count: 10, repeatedValue: 18))Và bạn thực sự nên nâng cấp lên một bản beta mới hơn. Int[][]()không còn là cú pháp hợp lệ. Nó đã được đổi thành [[Int]]().
Mick MacCallum

1
Đơn vị 2D sử dụng các giá trị lặp lại sẽ không hoạt động. Tất cả các hàng sẽ trỏ đến cùng một mảng con, và do đó không thể ghi duy nhất.
hotpaw2

Câu trả lời:


228

Xác định mảng có thể thay đổi

// 2 dimensional array of arrays of Ints 
var arr = [[Int]]() 

HOẶC LÀ:

// 2 dimensional array of arrays of Ints 
var arr: [[Int]] = [] 

HOẶC nếu bạn cần một mảng có kích thước được xác định trước (như được đề cập bởi @ 0x7fffffff trong nhận xét):

// 2 dimensional array of arrays of Ints set to 0. Arrays size is 10x5
var arr = Array(count: 3, repeatedValue: Array(count: 2, repeatedValue: 0))

// ...and for Swift 3+:
var arr = Array(repeating: Array(repeating: 0, count: 2), count: 3)

Thay đổi phần tử ở vị trí

arr[0][1] = 18

HOẶC LÀ

let myVar = 18
arr[0][1] = myVar

Thay đổi mảng phụ

arr[1] = [123, 456, 789] 

HOẶC LÀ

arr[0] += 234

HOẶC LÀ

arr[0] += [345, 678]

Nếu bạn có mảng 3x2 gồm 0 (số không) trước những thay đổi này, thì bây giờ bạn có:

[
  [0, 0, 234, 345, 678], // 5 elements!
  [123, 456, 789],
  [0, 0]
]

Vì vậy, hãy lưu ý rằng các mảng con có thể thay đổi và bạn có thể xác định lại mảng ban đầu là ma trận đại diện.

Kiểm tra kích thước / giới hạn trước khi truy cập

let a = 0
let b = 1

if arr.count > a && arr[a].count > b {
    println(arr[a][b])
}

Nhận xét: Quy tắc đánh dấu giống nhau cho mảng 3 và N chiều.


ok một câu hỏi ngớ ngẩn: chúng ta gán mảng đó như thế nào, Trong C chúng ta làm như thế: arr [i] [j] = myVar; nhưng trong nhanh chóng khi tôi cố gắng để làm cùng một cách mà tôi đã nhận lỗi này "không có một thành viên có tên là 'subscript' '[([(Int)])] Gõ.'"
Antiokhos

Nếu bạn đã arrxác định như trong câu trả lời thì myVarnên là Int, phải không?
Keenle

vâng nó là int. Và cảm ơn bạn rất nhiều vì câu trả lời chi tiết .. bây giờ nó rõ ràng: D
Antiokhos

6
Trong Swift 3, dành cho trình sao chép:var arr = Int(repeating: Int(repeating: 0, count: 2), count: 3)
kar

1
Trong Swift 4.2: ví dụ, 3 liên tiếp, 2 cột, 3 * 2var arr = Array(count: 2, repeatedValue: Array(count: 3, repeatedValue: 0))
Zgpeace

27

Từ các tài liệu:

Bạn có thể tạo mảng nhiều chiều bằng cách lồng các cặp dấu ngoặc vuông, trong đó tên của loại cơ sở của phần tử được chứa trong cặp dấu ngoặc vuông trong cùng. Ví dụ: bạn có thể tạo một mảng ba chiều các số nguyên bằng cách sử dụng ba bộ dấu ngoặc vuông:

var array3D: [[[Int]]] = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]

Khi truy cập các phần tử trong một mảng nhiều chiều, chỉ số chỉ số con ngoài cùng bên trái tham chiếu đến phần tử tại chỉ mục đó trong mảng ngoài cùng. Chỉ mục chỉ số con tiếp theo ở bên phải đề cập đến phần tử ở chỉ mục đó trong mảng được lồng vào một cấp. Và cứ thế. Điều này có nghĩa là trong ví dụ trên, array3D [0] tham chiếu đến [[1, 2], [3, 4]], array3D [0] [1] tham chiếu đến [3, 4] và array3D [0] [1 ] [1] đề cập đến giá trị 4.


17

Đặt nó là Generic Swift 4

struct Matrix<T> {
    let rows: Int, columns: Int
    var grid: [T]
    init(rows: Int, columns: Int,defaultValue: T) {
        self.rows = rows
        self.columns = columns
        grid = Array(repeating: defaultValue, count: rows * columns) as! [T]
    }
    func indexIsValid(row: Int, column: Int) -> Bool {
        return row >= 0 && row < rows && column >= 0 && column < columns
    }
    subscript(row: Int, column: Int) -> T {
        get {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            return grid[(row * columns) + column]
        }
        set {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            grid[(row * columns) + column] = newValue
        }
    }
}


var matrix:Matrix<Bool> = Matrix(rows: 1000, columns: 1000,defaultValue:false)

matrix[0,10] = true


print(matrix[0,10])

Tôi đã điều chỉnh câu trả lời của bạn để tạo một mảng hình xuyến 2D. Cảm ơn rất nhiều! gist.github.com/amiantos/bb0f313da1ee686f4f69b8b44f3cd184
Brad Root

16

Bạn nên cẩn thận khi sử dụng Array(repeating: Array(repeating: {value}, count: 80), count: 24).

Nếu giá trị là một đối tượng, được khởi tạo bởi MyClass(), thì chúng sẽ sử dụng cùng một tham chiếu.

Array(repeating: Array(repeating: MyClass(), count: 80), count: 24)không tạo một phiên bản mới của MyClassmỗi phần tử mảng. Phương thức này chỉ tạo MyClassmột lần và đưa nó vào mảng.

Đây là một cách an toàn để khởi tạo một mảng đa chiều.

private var matrix: [[MyClass]] = MyClass.newMatrix()

private static func newMatrix() -> [[MyClass]] {
    var matrix: [[MyClass]] = []

    for i in 0...23 {
        matrix.append( [] )

        for _ in 0...79 {
            matrix[i].append( MyClass() )
        }
    }

    return matrix
}

Xin chào, chúng tôi có thể cải thiện điều đó dưới dạng tiện ích mở rộng với loại "anyObject" không?
Antiokhos

Điểm tốt về vấn đề với các loại tài liệu tham khảo. Tuy nhiên, tại sao bạn viết Array(repeating: {value}, could 80)với dấu ngoặc nhọn xung quanh {value}? Điều đó sẽ tạo ra một loạt các bao đóng, phải không?
Duncan C

Hay là {value}ký hiệu meta cho "một số giá trị của loại AnyObject" (một loại tham chiếu)?
Duncan C

Tôi đã dành gần một giờ tìm kiếm một lỗi vì vấn đề này ...
Matheus Weber

13

Trong Swift 4

var arr = Array(repeating: Array(repeating: 0, count: 2), count: 3)
// [[0, 0], [0, 0], [0, 0]]

10

Theo tài liệu của Apple cho swift 4.1, bạn có thể sử dụng cấu trúc này rất dễ dàng để tạo một mảng 2D:

Liên kết: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Subscripts.html

Mẫu mã:

struct Matrix {
    let rows: Int, columns: Int
    var grid: [Double]
    init(rows: Int, columns: Int) {
        self.rows = rows
        self.columns = columns
        grid = Array(repeating: 0.0, count: rows * columns)
    }
    func indexIsValid(row: Int, column: Int) -> Bool {
        return row >= 0 && row < rows && column >= 0 && column < columns
    }
    subscript(row: Int, column: Int) -> Double {
        get {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            return grid[(row * columns) + column]
        }
        set {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            grid[(row * columns) + column] = newValue
        }
    }
}

1
Tôi thích nó. Nó gợi nhớ đến số học con trỏ C. Sẽ tốt hơn nếu được viết lại bằng Generics, vì vậy nó sẽ áp dụng cho các mảng 2 chiều của bất kỳ kiểu dữ liệu nào. Đối với vấn đề đó, bạn có thể sử dụng cách tiếp cận này để tạo mảng bất kỳ thứ nguyên tùy ý nào.
Duncan C

1
@vacawama, thật tuyệt, ngoại trừ mảng n-chiều của bạn có cùng một vấn đề với tất cả các giải pháp sử dụng mảng Array(repeating:count:). Xem bình luận tôi đã đăng cho câu trả lời khác của bạn.
Duncan C

6

Trước khi sử dụng mảng đa chiều trong Swift, hãy xem xét tác động của chúng đến hiệu suất . Trong các thử nghiệm của tôi, mảng phẳng hoạt động tốt hơn gần gấp đôi so với phiên bản 2D:

var table = [Int](repeating: 0, count: size * size)
let array = [Int](1...size)
for row in 0..<size {
    for column in 0..<size {
        let val = array[row] * array[column]
        // assign
        table[row * size + column] = val
    }
}

Thời gian thực hiện trung bình để lấp đầy Mảng 50x50: 82,9ms

so với

var table = [[Int]](repeating: [Int](repeating: 0, count: size), count: size)
let array = [Int](1...size)
for row in 0..<size {
    for column in 0..<size {
        // assign
        table[row][column] = val
    }
}

Thời gian thực hiện trung bình để lấp đầy Mảng 2D 50x50: 135ms

Cả hai thuật toán đều là O (n ^ 2), vì vậy sự khác biệt về thời gian thực thi là do cách chúng ta khởi tạo bảng.

Cuối cùng, điều tồi tệ nhất bạn có thể làm là sử dụng append()để thêm các phần tử mới. Điều đó được chứng minh là chậm nhất trong các thử nghiệm của tôi:

var table = [Int]()    
let array = [Int](1...size)
for row in 0..<size {
    for column in 0..<size {
        table.append(val)
    }
}

Thời gian thực thi trung bình để lấp đầy Mảng 50x50 bằng append (): 2,59 giây

Phần kết luận

Tránh các mảng nhiều chiều và sử dụng quyền truy cập theo chỉ mục nếu tốc độ thực thi có vấn đề. Mảng 1D có hiệu suất cao hơn, nhưng mã của bạn có thể khó hiểu hơn một chút.

Bạn có thể tự chạy các bài kiểm tra hiệu suất sau khi tải xuống dự án demo từ repo GitHub của tôi: https://github.com/nyisztor/swift-algorithm/tree/master/big-o-src/Big-O.playground


0

Điều này có thể được thực hiện trong một dòng đơn giản.

Swift 5

var my2DArray = (0..<4).map { _ in Array(0..<) }

Bạn cũng có thể ánh xạ nó đến các phiên bản của bất kỳ lớp hoặc cấu trúc nào bạn chọn

struct MyStructCouldBeAClass {
    var x: Int
    var y: Int
}

var my2DArray: [[MyStructCouldBeAClass]] = (0..<2).map { x in
    Array(0..<2).map { MyStructCouldBeAClass(x: x, y: $0)}
}
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.