Dwuwymiarowa tablica w Swift

109

Jestem tak zdezorientowany co do tablic 2D w Swift. Opiszę krok po kroku. Czy mógłbyś mnie poprawić, jeśli się mylę.

Po pierwsze; deklaracja pustej tablicy:

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

Po drugie wypełnij tablicę. (np. my2Darr[i][j] = 0gdzie i, j są zmiennymi pętli for)

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

I na koniec Edycja elementu in array

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
źródło
Masz problemy ze swoim podejściem?
Alex Wayne
2
Pamiętaj, że cały twój drugi krok można sprowadzić do tego. var my2DArray = Array(count: 10, repeatedValue: Array(count: 10, repeatedValue: 18))Naprawdę powinieneś uaktualnić do nowszej wersji beta. Int[][]()nie jest już prawidłową składnią. Zostało zmienione na [[Int]]().
Mick MacCallum
1
Init 2D używający powtarzających się wartości nie zadziała. Wszystkie wiersze będą wskazywać na tę samą tablicę podrzędną, a zatem nie będą miały niepowtarzalnego zapisu.
hotpaw2

Odpowiedzi:

228

Zdefiniuj zmienną tablicę

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

LUB:

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

LUB jeśli potrzebujesz tablicy o predefiniowanym rozmiarze (jak wspomniano w komentarzach @ 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)

Zmień element na pozycji

arr[0][1] = 18

LUB

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

Zmień macierz podrzędną

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

LUB

arr[0] += 234

LUB

arr[0] += [345, 678]

Gdybyś miał tablicę 3x2 0 (zer) przed tymi zmianami, teraz masz:

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

Należy więc pamiętać, że tablice podrzędne są zmienne i można przedefiniować początkową tablicę reprezentującą macierz.

Sprawdź rozmiar / granice przed dostępem

let a = 0
let b = 1

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

Uwagi: Te same reguły znaczników dla tablic 3 i N wymiarowych.

Keenle
źródło
ok, jedno głupie pytanie: jak przypisujemy tę tablicę, w C robimy tak: arr [i] [j] = myVar; ale w szybkich, gdy próbuję zrobić tak samo mam ten błąd „nie ma członkiem nazwie«indeks» '[([(int)])] Typ”.
Antiokhos
Jeśli arrzdefiniowałeś tak jak w odpowiedzi, to myVarpowinno być Int, prawda?
Keenle,
tak to jest int. I bardzo dziękuję za szczegółową odpowiedź… teraz wszystko jasne: D
Antiokhos
6
W Swift 3, dla kserokopiarzy:var arr = Int(repeating: Int(repeating: 0, count: 2), count: 3)
kar
1
W Swift 4.2: na przykład 3 rzędy, 2 kolumny, 3 * 2var arr = Array(count: 2, repeatedValue: Array(count: 3, repeatedValue: 0))
Zgpeace
27

Z dokumentów:

Możesz tworzyć tablice wielowymiarowe, zagnieżdżając pary nawiasów kwadratowych, w których nazwa podstawowego typu elementów jest zawarta w najbardziej wewnętrznej parze nawiasów kwadratowych. Na przykład możesz utworzyć trójwymiarową tablicę liczb całkowitych, używając trzech zestawów nawiasów kwadratowych:

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

Podczas uzyskiwania dostępu do elementów w tablicy wielowymiarowej, skrajny lewy indeks indeksu dolnego odnosi się do elementu w tym indeksie w najbardziej zewnętrznej tablicy. Następny indeks dolny po prawej stronie odnosi się do elementu w tym indeksie tablicy, który jest zagnieżdżony na jednym poziomie. I tak dalej. Oznacza to, że w powyższym przykładzie tablica3D [0] odnosi się do [[1, 2], [3, 4]], tablica3D [0] [1] odnosi się do [3, 4], a tablica3D [0] [1 ] [1] odnosi się do wartości 4.

Woodstock
źródło
17

Niech to będzie 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])
dimo hamdy
źródło
Dostosowałem twoją odpowiedź, aby utworzyć tablicę toroidalną 2D. Wielkie dzięki! gist.github.com/amiantos/bb0f313da1ee686f4f69b8b44f3cd184
Brad Root
16

Należy zachować ostrożność podczas używania Array(repeating: Array(repeating: {value}, count: 80), count: 24).

Jeśli wartością jest obiekt, który jest inicjowany przez MyClass(), wtedy użyją tego samego odwołania.

Array(repeating: Array(repeating: MyClass(), count: 80), count: 24)nie tworzy nowej instancji MyClassw każdym elemencie tablicy. Ta metoda tworzy tylko MyClassraz i umieszcza ją w tablicy.

Oto bezpieczny sposób na zainicjowanie tablicy wielowymiarowej.

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
źródło
Cześć, czy możemy to ulepszyć jako rozszerzenie z typem „anyObject”?
Antiokhos
Dobra uwaga na temat problemu z typami referencyjnymi. Dlaczego jednak piszesz Array(repeating: {value}, could 80)w nawiasach klamrowych {value}? To stworzyłoby szereg zamknięć, prawda?
Duncan C,
A może {value}meta-notacja dla „jakiejś wartości typu AnyObject” (typ referencyjny)?
Duncan C,
Spędziłem prawie godzinę szukając błędu z powodu tego problemu ...
Matheus Weber
13

W Swift 4

var arr = Array(repeating: Array(repeating: 0, count: 2), count: 3)
// [[0, 0], [0, 0], [0, 0]]
Ankit garg
źródło
10

Zgodnie z dokumentami Apple dla Swift 4.1 możesz tak łatwo użyć tej struktury do utworzenia tablicy 2D:

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

Przykład kodu:

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
źródło
1
Lubię to. Przypomina arytmetykę wskaźnika C. Byłoby jednak lepiej, gdyby przepisano go za pomocą Generics, aby można go było zastosować do dwuwymiarowych tablic dowolnego typu danych. W tym przypadku możesz użyć tego podejścia do tworzenia tablic o dowolnym dowolnym wymiarze.
Duncan C,
1
@vacawama, fajnie, z wyjątkiem tego, że twoja n-wymiarowa tablica ma ten sam problem, co wszystkie rozwiązania, które wypełniają tablicę za pomocą Array(repeating:count:). Zobacz komentarz, który zamieściłem do Twojej drugiej odpowiedzi.
Duncan C,
6

Przed użyciem tablic wielowymiarowych w Swift należy rozważyć ich wpływ na wydajność . W moich testach spłaszczona tablica wypadła prawie 2x lepiej niż wersja 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
    }
}

Średni czas wykonywania zapełnienia tablicy 50x50: 82,9 ms

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

Średni czas wykonywania wypełnienia tablicy 2D 50x50: 135 ms

Oba algorytmy mają wartość O (n ^ 2), więc różnica w czasie wykonywania jest spowodowana sposobem, w jaki inicjalizujemy tabelę.

Wreszcie, najgorsze, co możesz zrobić, to append()dodać nowe elementy. To okazało się najwolniejsze w moich testach:

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

Średni czas wykonywania wypełniania tablicy 50x50 za pomocą funkcji append (): 2,59 s

Wniosek

Unikaj tablic wielowymiarowych i korzystaj z dostępu według indeksu, jeśli liczy się szybkość wykonywania. Tablice 1D są bardziej wydajne, ale Twój kod może być nieco trudniejszy do zrozumienia.

Możesz samodzielnie przeprowadzić testy wydajności po pobraniu projektu demonstracyjnego z mojego repozytorium GitHub: https://github.com/nyisztor/swift-algorithms/tree/master/big-o-src/Big-O.playground

Karoly Nyisztor
źródło
0

Można to zrobić w jednej prostej linii.

Szybki 5

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

Możesz również zmapować go na wystąpienia dowolnej wybranej klasy lub struktury

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
źródło