代码之家  ›  专栏  ›  技术社区  ›  zoecarver

n维阵列swift

  •  1
  • zoecarver  · 技术社区  · 6 年前

    有没有办法在swift中使用n维数组?我希望能够创建一个函数来创建一个n维数组,但是我不知道如何创建。

    基本上是这样的:

    func ndarray <T> (dimensions: Int...) -> [[T]] { // What do I tell it I return?
        var out
        for d in dimensions {
            out = Array<T>(repeating: out, count: d)
        }
        return out
    } 
    

    由于显而易见的原因,上面的代码不起作用,但我认为它指出了我遇到的主要问题:

    • 如何定义返回类型
    • 如何实际创建数组
    • 创建后,如何遍历和填充数组
    2 回复  |  直到 6 年前
        1
  •  3
  •   vacawama    6 年前

    这是一个n维数组的实现。它在内部使用普通数组进行存储,并将多维索引转换为内部数组的单个索引。

    struct NDimArray<T> {
        let dimensions: [Int]
        var data: [T]
    
        init(dimensions: Int..., initialValue: T) {
            self.dimensions = dimensions
            data = Array(repeating: initialValue, count: dimensions.reduce(1, *))
        }
    
        init(dimensions: Int..., initUsing initializer: () -> T) {
            self.dimensions = dimensions
            data = (0 ..< dimensions.reduce(1, *)).map { _ in initializer() }
        }
    
        // Compute index into data from indices
        private func computeIndex(_ indices: [Int]) -> Int {
            guard indices.count == dimensions.count else { fatalError("Wrong number of indices: got \(indices.count), expected \(dimensions.count)") }
            zip(dimensions, indices).forEach { dim, idx in
                guard (0 ..< dim) ~= idx else { fatalError("Index out of range") }
            }
    
            var idx = indices
            var dims = dimensions
            var product = 1
            var total = idx.removeLast()
            while !idx.isEmpty {
                product *= dims.removeLast()
                total += (idx.removeLast() * product)
            }
    
            return total
        }
    
        subscript(_ indices: Int...) -> T {
            get {
                return data[computeIndex(indices)]
            }
            set {
                data[computeIndex(indices)] = newValue
            }
        }
    }
    

    例子:

    // Create a 3 x 4 x 5 array of String with initial value ""
    
    var arr = NDimArray<String>(dimensions: 3, 4, 5, initialValue: "")
    for x in 0 ..< 3 {
        for y in 0 ..< 4 {
            for z in 0 ..< 5 {
                // Encode indices in the string
                arr[x, y, z] = "(\(x),\(y),\(z))"
            }
        }
    }
    
    
    // Show internal storage of data
    print(arr.data)
    

    “(0,0,0,0,0,0,0,0,1)”,“(0,0,0,0,0,0,0,0,0,0,1,1,“(0,1,1,1,1)”,“(0,1,2)”,“(0,1,1,2)”,“(0,1,3)”,“(0,1,4)”,“(0,1,0,2,0)”,“(0,2,2,2)”,“(0,2,3,3)”,“(0,0,0,0,0,0,1,“(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3)、“(0,3,4)”、“(1,0,0)”、“(1,0,1)”、“(1,0,2)”、“(1,0,3)”、“(1,0,4)”、“(1,1,0)”、“(1,1,1)”、“(1,1,2)”、“(1,1,3)”、“(1,1,4)”、“(1,2,0)”、“(1,2”,1)", "(1,2,2)", "(1,2,3)", "(1,2,4)", "(1,3,0)", "(1,3,1)", "(1,3,2)", "(1,3,3)", "(1,3,4)", "(2,0,0)", "(2,0,1)", "(2,0,2)", "(2,0,3)", "(2,0,4)", "(2,1,0)", "(2,1,1)", "(2,1,2)", "(2,1,3)", "(2,1,4)", "(2,2,0)", "(2,2,1)", "(2,2,2)", "(2,2,3)", "(2,2,4)", "(2,3,0)", "(2,3,1)", "(2,3,2)", "(2,3,3)", "(2,3,4)"]

    print(arr[2, 2, 2])  // "(2,2,2)"
    print(arr[3, 0, 0])  // Fatal error: Index out of range
    print(arr[0, 4, 0])  // Fatal error: Index out of range
    print(arr[2])        // Fatal error: Wrong number of indices: got 1, expected 3
    

    使用引用类型初始化数组

    正如@DuncanC在注释中所指出的,在使用引用类型的值初始化数组时必须小心,因为数组将充满对对象的引用,并且在任何索引处修改对象都将修改所有这些引用。

    为了解决这个问题,我添加了第二个初始值设定项:

    init(dimensions: Int..., initUsing initializer: () -> T)
    

    就这样结束了 () -> T 它可用于为数组的每个元素创建新对象。

    例如:

    class Person {
        var name = ""
    }
    
    // Pass a closure which creates a `Person` instance to fill the array
    // with 25 person objects   
    let arr = NDimArray(dimensions: 5, 5, initUsing: { Person() })
    arr[3, 3].name = "Fred"
    arr[2, 2].name = "Wilma"
    
    print(arr[3, 3].name, arr[2, 2].name)
    

    弗雷德威尔玛

        2
  •  0
  •   Cristik    6 年前

    不,不可能。数组维度需要在编译时确定,而要传递给初始值设定项的参数在运行时才会知道。如果你真的想要达到这样的效果,那么你需要把数组索引从编译时间移动到运行时,例如通过索引数组访问数组。仍然没有编译验证,因为数组长度在运行时可能与数组的维数不匹配。

    这个问题类似于试图将元组转换为数组的问题。