Двовимірний масив у Свіфті


109

Я так заплутався щодо 2D-масивів у Swift. Дозвольте описати крок за кроком. А ви хочете виправити мене, якщо я помиляюся.

Поперше; оголошення порожнього масиву:

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

По-друге заповніть масив. (наприклад, my2Darr[i][j] = 0де i, j - змінні для циклу)

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

І нарешті, Редагування елемента масиву

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

Чи є у вас проблеми зі своїм підходом?
Алекс Уейн

2
Просто так ви знаєте, весь ваш другий крок можна звести до цього. var my2DArray = Array(count: 10, repeatedValue: Array(count: 10, repeatedValue: 18))І вам слід дійсно оновити до нової бета-версії. Int[][]()більше не є синтаксисом. Це було змінено на [[Int]]().
Мік Маккаллум

1
2D-ініт із повторними значеннями не працюватиме. Усі рядки будуть вказувати на один і той же підмасив, і таким чином не можна однозначно записувати.
hotpaw2

Відповіді:


228

Визначте змінний масив

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

АБО:

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

АБО якщо вам потрібен масив заздалегідь заданого розміру (як згадується @ 0x7fffffff у коментарях):

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

Змінити елемент на місці

arr[0][1] = 18

АБО

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

Змінити підмасив

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

АБО

arr[0] += 234

АБО

arr[0] += [345, 678]

Якщо до цих змін у вас був масив 3x2 0 (нулі), тепер у вас є:

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

Тому майте на увазі, що підмасиви є змінними, і ви можете переглядати початковий масив, який представляв матрицю.

Вивчіть розмір / межі перед доступом

let a = 0
let b = 1

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

Зауваження: однакові правила розмітки для 3 та N розмірних масивів.


ОК одне дурне питання: як ми призначаємо цей масив, В C ми робимо так: arr [i] [j] = myVar; але швидко, коли я намагаюся зробити так само, я отримав цю помилку "" [([(Int)])]. У типу "немає учасника з ім'ям" індекс ""
Антіохос

Якщо ви arrвизначили, як у відповіді, тоді myVarмає бути Int, чи не так?
Кінле

так, це int. І дуже дякую за детальну відповідь .. тепер зрозуміло: D
Антіохос

6
У Swift 3 для пастерів для копіювання:var arr = Int(repeating: Int(repeating: 0, count: 2), count: 3)
kar

1
У Swift 4.2: наприклад, 3 ряд, 2 стовпчик, 3 * 2var arr = Array(count: 2, repeatedValue: Array(count: 3, repeatedValue: 0))
Zgpeace

27

З документів:

Можна створити багатовимірні масиви, вклавши пари квадратних дужок, де назва базового типу елементів міститься у самій внутрішній парі квадратних дужок. Наприклад, ви можете створити тривимірний масив цілих чисел, використовуючи три набори квадратних дужок:

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

Під час доступу до елементів у багатовимірному масиві, самий індекс підлітка індексу посилається на елемент у цьому індексі у найбільш зовнішньому масиві. Наступний індекс індексів праворуч посилається на елемент цього індексу в масиві, який вкладений один рівень дюйма. І так далі. Це означає, що у наведеному вище прикладі array3D [0] відноситься до [[1, 2], [3, 4]], array3D [0] [1] посилається на [3, 4], а масив3D [0] [1 ] [1] відноситься до значення 4.


17

Зробіть це загальним 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])

Я адаптував вашу відповідь, щоб створити 2D тороїдальний масив. Дуже дякую! gist.github.com/amiantos/bb0f313da1ee686f4f69b8b44f3cd184
Brad Root

16

Ви повинні бути обережними під час використання Array(repeating: Array(repeating: {value}, count: 80), count: 24).

Якщо значення є об'єктом, який ініціалізується MyClass(), то вони будуть використовувати ту саму посилання.

Array(repeating: Array(repeating: MyClass(), count: 80), count: 24)не створює новий екземпляр MyClassкожного елемента масиву. Цей метод створюється лише MyClassодин раз і ставить його в масив.

Ось безпечний спосіб ініціалізації багатовимірного масиву.

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
}

Привіт, чи можемо ми покращити це як розширення типу "anyObject"?
Антіохос

Хороша думка про проблему з еталонними типами. Однак чому ти пишеш Array(repeating: {value}, could 80)дужками навколо {value}? Це створило б масив закриттів, чи не так?
Дункан C

Або {value}мета-позначення для "деякого значення типу AnyObject" (тип посилання)?
Дункан C

Я витратив майже годину на пошуки помилок через цю проблему ...
Вебер


10

Згідно з документами Apple для swift 4.1, ви можете використовувати цю структуру так легко, щоб створити 2D масив:

Посилання: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Subscripts.html

Зразок коду:

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
Мені це подобається. Це нагадує арифметику вказівника С. Хоча було б краще переписати за допомогою Generics, тому воно застосовуватиметься до двовимірних масивів будь-якого типу даних. З цього питання ви можете використовувати цей підхід для створення масивів будь-якого довільного виміру.
Дункан C

1
@vacawama, круто, за винятком вашого n-мірного масиву є та ж проблема, що і всі рішення, які заповнюють масив, використовуючи Array(repeating:count:). Дивіться коментар, який я опублікував на вашу іншу відповідь.
Duncan C

6

Перш ніж використовувати багатовимірні масиви в Swift, врахуйте їх вплив на продуктивність . У моїх тестах сплющений масив працював майже в 2 рази краще, ніж у 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
    }
}

Середній час виконання для заповнення масиву 50x50: 82,9 мс

vs.

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

Середній час виконання для заповнення масиву 50D50 2D: 135ms

Обидва алгоритми є O (n ^ 2), тому різниця у часі виконання обумовлена ​​способом ініціалізації таблиці.

Нарешті, найгірше, що можна зробити, - append()це додавання нових елементів. Це виявилося найбільш повільним у моїх тестах:

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

Середній час виконання для заповнення масиву 50x50 за допомогою append (): 2,59s

Висновок

Уникайте багатовимірних масивів і використовуйте доступ за індексом, якщо важлива швидкість виконання. 1D масиви є більш ефективними, але ваш код може бути трохи складніше зрозуміти.

Ви можете самостійно запустити тести на ефективність після завантаження демонстраційного проекту з мого репортажу GitHub: https://github.com/nyisztor/swift-algorithms/tree/master/big-o-src/Big-O.playground


0

Це можна зробити одним простим рядком.

Швидкий 5

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

Ви також можете зіставити його з примірниками будь-якого класу або структури на ваш вибір

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)}
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.