繁体   English   中英

N维数组swift

[英]N-Dimensional array swift

有没有办法在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
} 

以上代码不适用于obvios原因,但我认为它指出了我遇到的主要问题:

  • 如何定义返回类型
  • 我如何实际创建数组
  • 一旦创建,我如何遍历和填充数组

这是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,1)”,“(0,0,2)”,“(0,0,3)”,“(0,0,4)” ,“(0,1,0)”,“(0,1,1)”,“(0,1,2)”,“(0,1,3)”,“(0,1,4)” ,“(0,2,0)”,“(0,2,1)”,“(0,2,2)”,“(0,2,3)”,“(0,2,4)” ,“(0,3,0)”,“(0,3,1)”,“(0,3,2)”,“(0,3,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)

它接受一个closure () -> 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)

弗雷德威尔玛

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

此问题类似于尝试将元组转换为数组的问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM