Двумерный массив в Swift

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) { ...  }   */
    }
}
Антиох
источник
У вас проблемы с вашим подходом?
Alex Wayne
2
Просто чтобы вы знали, весь ваш второй шаг можно свести к этому. var my2DArray = Array(count: 10, repeatedValue: Array(count: 10, repeatedValue: 18))И вам действительно следует перейти на более новую бета-версию. Int[][]()больше не является допустимым синтаксисом. Он был изменен на [[Int]]().
Mick MacCallum
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])
}

Примечания: одинаковые правила разметки для трехмерных и N-мерных массивов.

Кинл
источник
Хорошо, один пустяковый вопрос: как мы назначаем этот массив. В C мы делаем это так: arr [i] [j] = myVar; но быстро, когда я пытаюсь сделать то же самое, я получил эту ошибку «'[([(Int)])]. Тип' не имеет члена с именем 'subscript'»
Antiokhos
Если вы arrопределили, как в ответе, тогда myVarдолжно быть Int, не так ли?
Кинл 05
да это внутр. И большое спасибо за подробный ответ .. теперь все ясно: D
Antiokhos 05
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], а array3D [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])
Димо Хамди
источник
Я адаптировал ваш ответ для создания двумерного тороидального массива. Большое спасибо! gist.github.com/amiantos/bb0f313da1ee686f4f69b8b44f3cd184
Брэд Рут
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?
Antiokhos
Хороший момент о проблеме со ссылочными типами. Однако почему вы пишете Array(repeating: {value}, could 80)скобками {value}? Это создало бы массив закрытий, не так ли?
Duncan C
Или это {value}мета-нотация для «некоторого значения типа AnyObject» (ссылочный тип)?
Duncan C
Я потратил почти час на поиск ошибки из-за этой проблемы ...
Матеус Вебер
13

В Swift 4

var arr = Array(repeating: Array(repeating: 0, count: 2), count: 3)
// [[0, 0], [0, 0], [0, 0]]
Анкит Гарг
источник
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
Мне это нравится. Это напоминает арифметику указателя C. Было бы лучше, если бы он был переписан с использованием Generics, чтобы он применялся к двумерным массивам любого типа данных. В этом отношении вы можете использовать этот подход для создания массивов любого произвольного измерения.
Duncan 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
    }
}

Среднее время выполнения для заполнения 2D-массива 50x50: 135 мс

Оба алгоритма имеют размер 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,59 с.

Вывод

Избегайте многомерных массивов и используйте доступ по индексу, если скорость выполнения имеет значение. Одномерные массивы более производительны, но ваш код может быть немного сложнее для понимания.

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

Кароли Ништор
источник
0

Это можно сделать одной простой строкой.

Swift 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)}
}
пимиси
источник