Matriz bidimensional em Swift

109

Eu fico tão confuso sobre arrays 2D no Swift. Deixe-me descrever passo a passo. E você poderia me corrigir se eu estiver errado.

Em primeiro lugar; declaração de uma matriz vazia:

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

Em segundo lugar, preencha a matriz. (como my2Darr[i][j] = 0onde i, j são variáveis ​​for-loop)

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

E por último, Editando o elemento da matriz

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) { ...  }   */
    }
}
Antiokhos
fonte
Você está tendo problemas com sua abordagem?
Alex Wayne
2
Só para você saber, toda a sua segunda etapa pode ser reduzida a isso. var my2DArray = Array(count: 10, repeatedValue: Array(count: 10, repeatedValue: 18))E você realmente deve atualizar para uma versão beta mais recente. Int[][]()não é mais sintaxe válida. Foi alterado para [[Int]]().
Mick MacCallum
1
A inicialização 2D usando valores repetidos não funcionará. Todas as linhas apontarão para a mesma submatriz e, portanto, não serão graváveis ​​exclusivamente.
hotpaw2

Respostas:

228

Definir matriz mutável

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

OU:

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

OU se você precisar de uma matriz de tamanho predefinido (conforme mencionado por @ 0x7fffffff nos comentários):

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

Mudar elemento na posição

arr[0][1] = 18

OU

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

Alterar submatriz

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

OU

arr[0] += 234

OU

arr[0] += [345, 678]

Se você tinha uma matriz 3x2 de 0 (zeros) antes dessas mudanças, agora você tem:

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

Portanto, esteja ciente de que as submatrizes são mutáveis ​​e você pode redefinir a matriz inicial que representa a matriz.

Examine o tamanho / limites antes de acessar

let a = 0
let b = 1

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

Observações: Mesmas regras de marcação para matrizes 3 e N dimensionais.

Keenle
fonte
ok, uma pergunta fictícia: como atribuímos esse array, em C fazemos assim: arr [i] [j] = myVar; mas rapidamente, quando tento fazer a mesma coisa, recebo este erro "'[([(Int)])]. Tipo' não tem um membro chamado 'subscrito'"
Antiokhos
Se você arrdefiniu como na resposta, então myVardeveria ser Int, não é?
Keenle de
sim, é int. E muito obrigado pela resposta detalhada .. agora está claro: D
Antiokhos
6
Em Swift 3, para copiar pasters:var arr = Int(repeating: Int(repeating: 0, count: 2), count: 3)
kar
1
No Swift 4.2: por exemplo, 3 linhas, 2 colunas, 3 * 2var arr = Array(count: 2, repeatedValue: Array(count: 3, repeatedValue: 0))
Zgpeace
27

Dos documentos:

Você pode criar matrizes multidimensionais aninhando pares de colchetes, onde o nome do tipo básico dos elementos está contido no par mais interno de colchetes. Por exemplo, você pode criar uma matriz tridimensional de inteiros usando três conjuntos de colchetes:

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

Ao acessar os elementos em uma matriz multidimensional, o índice subscrito mais à esquerda se refere ao elemento naquele índice na matriz mais externa. O próximo índice subscrito à direita se refere ao elemento nesse índice na matriz que está aninhado em um nível. E assim por diante. Isso significa que no exemplo acima, array3D [0] se refere a [[1, 2], [3, 4]], array3D [0] [1] se refere a [3, 4] e array3D [0] [1 ] [1] refere-se ao valor 4.

Woodstock
fonte
17

Torne-o Genérico 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])
Dimo Hamdy
fonte
Adaptei sua resposta para criar uma matriz toroidal 2D. Muito obrigado! gist.github.com/amiantos/bb0f313da1ee686f4f69b8b44f3cd184
Brad Root
16

Você deve ter cuidado ao usar Array(repeating: Array(repeating: {value}, count: 80), count: 24).

Se o valor for um objeto, que é inicializado por MyClass(), eles usarão a mesma referência.

Array(repeating: Array(repeating: MyClass(), count: 80), count: 24)não cria uma nova instância de MyClassem cada elemento da matriz. Este método cria apenas MyClassuma vez e o coloca na matriz.

Esta é uma maneira segura de inicializar um array multidimensional.

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
}
Kimi Chiu
fonte
Olá, podemos melhorar isso como uma extensão do tipo "anyObject"?
Antiokhos
Bom ponto sobre o problema com tipos de referência. No entanto, por que você escreve Array(repeating: {value}, could 80)com colchetes {value}? Isso criaria uma série de fechamentos, não é?
Duncan C
Ou é uma {value}meta-notação para "algum valor do tipo AnyObject" (um tipo de referência)?
Duncan C
Passei quase uma hora procurando por um bug por causa desse problema ...
Matheus Weber
13

Em Swift 4

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

De acordo com os documentos da Apple para o Swift 4.1, você pode usar essa estrutura facilmente para criar um array 2D:

Link: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Subscripts.html

Amostra de código:

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
        }
    }
}
Keyhan Kamangar
fonte
1
Eu gosto disso. É uma reminiscência da aritmética de ponteiro C. No entanto, seria melhor se reescrito usando Genéricos, para que se aplicasse a matrizes bidimensionais de qualquer tipo de dados. Por falar nisso, você poderia usar essa abordagem para criar matrizes de qualquer dimensão arbitrária.
Duncan C
1
@vacawama, legal, exceto que seu array n-dimensional tem o mesmo problema que todas as soluções que preenchem o array usando Array(repeating:count:). Veja o comentário que postei na sua outra resposta.
Duncan C
6

Antes de usar arrays multidimensionais em Swift, considere seu impacto no desempenho . Em meus testes, o array achatado teve um desempenho quase 2x melhor do que a versão 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
    }
}

Tempo médio de execução para preencher um array 50x50: 82,9ms

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

Tempo médio de execução para preencher um array 2D 50x50: 135ms

Ambos os algoritmos são O (n ^ 2), então a diferença nos tempos de execução é causada pela maneira como inicializamos a tabela.

Finalmente, o pior que você pode fazer é append()adicionar novos elementos. Isso provou ser o mais lento em meus testes:

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

Tempo médio de execução para preencher uma matriz 50x50 usando append (): 2,59s

Conclusão

Evite arrays multidimensionais e use o acesso por índice se a velocidade de execução for importante. Os arrays 1D têm melhor desempenho, mas seu código pode ser um pouco mais difícil de entender.

Você mesmo pode executar os testes de desempenho após baixar o projeto de demonstração do meu repositório GitHub: https://github.com/nyisztor/swift-algorithms/tree/master/big-o-src/Big-O.playground

Karoly Nyisztor
fonte
0

Isso pode ser feito em uma linha simples.

Swift 5

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

Você também pode mapeá-lo para instâncias de qualquer classe ou estrutura de sua escolha

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)}
}
Pimisi
fonte