อาร์เรย์สองมิติใน Swift


109

ฉันสับสนมากเกี่ยวกับอาร์เรย์ 2D ใน Swift ให้ฉันอธิบายทีละขั้นตอน และคุณช่วยแก้ไขฉันได้ไหมถ้าฉันผิด

ก่อนอื่น; การประกาศอาร์เรย์ว่าง:

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

ประการที่สองเติมอาร์เรย์ (เช่นโดยmy2Darr[i][j] = 0ที่ i, j เป็นตัวแปร 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?  */
            }
        }
    }
}

และสุดท้ายคือการแก้ไของค์ประกอบในอาร์เรย์

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

หมายเหตุ: กฎมาร์กอัปเดียวกันสำหรับอาร์เรย์มิติ 3 และ N


ตกลงคำถามหลอกหนึ่งคำถาม: เรากำหนดอาร์เรย์นั้นอย่างไรใน C เราทำเช่นนั้น: arr [i] [j] = myVar; แต่อย่างรวดเร็วเมื่อฉันพยายามทำเช่นเดียวกันฉันได้รับข้อผิดพลาดนี้ "'[([(Int)])] ประเภท" ไม่มีสมาชิกชื่อ "ตัวห้อย"
Antiokhos

ถ้าคุณarrกำหนดไว้ในคำตอบแล้วmyVarควรเป็น Int ใช่หรือไม่?
Keenle

ใช่มันเป็น int และขอบคุณมากสำหรับคำตอบโดยละเอียด .. ตอนนี้ชัดเจนแล้ว: D
Antiokhos

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

ทำให้เป็น 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])

ฉันปรับคำตอบของคุณเพื่อสร้างอาร์เรย์ toroidal 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" ได้ไหม
Antiokhos

ประเด็นที่ดีเกี่ยวกับปัญหาเกี่ยวกับประเภทการอ้างอิง อย่างไรก็ตามทำไมคุณถึงเขียนArray(repeating: {value}, could 80)ด้วยเครื่องหมายวงเล็บ{value}? ที่จะสร้างอาร์เรย์ของการปิดมันจะไม่?
Duncan C

หรือเป็น{value}meta-notation สำหรับ "some value of type AnyObject" (a reference type)?
Duncan C

ฉันใช้เวลาเกือบชั่วโมงในการค้นหาจุดบกพร่องเนื่องจากปัญหานี้ ...
Matheus Weber


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 ดังนั้นจึงใช้กับอาร์เรย์ 2 มิติของข้อมูลประเภทใดก็ได้ สำหรับเรื่องนั้นคุณสามารถใช้แนวทางนี้เพื่อสร้างอาร์เรย์มิติใดก็ได้ตามอำเภอใจ
Duncan C

1
@vacawama เจ๋งยกเว้นอาร์เรย์ n มิติของคุณมีปัญหาเดียวกับโซลูชันทั้งหมดที่เติมอาร์เรย์โดยใช้Array(repeating:count:). ดูความคิดเห็นที่ฉันโพสต์ไว้สำหรับคำตอบอื่น ๆ ของคุณ
Duncan C

6

ก่อนที่จะใช้อาร์เรย์หลายมิติในสวิฟท์, พิจารณาผลกระทบต่อประสิทธิภาพการทำงานของพวกเขา ในการทดสอบของฉันอาร์เรย์แบบแบนทำงานได้ดีกว่าเวอร์ชัน 2D เกือบ 2 เท่า:

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.9ms

เทียบกับ

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: 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.59 วินาที

สรุป

หลีกเลี่ยงอาร์เรย์หลายมิติและใช้การเข้าถึงโดยดัชนีหากความเร็วในการดำเนินการมีความสำคัญ อาร์เรย์ 1D มีประสิทธิภาพมากกว่า แต่โค้ดของคุณอาจเข้าใจยากกว่าเล็กน้อย

คุณสามารถเรียกใช้การทดสอบประสิทธิภาพด้วยตัวเองหลังจากดาวน์โหลดโครงการสาธิตจาก repo 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)}
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.