有什么办法可以快速建立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
}
以上代码出于种种原因而无法正常运行,但我认为它指出了我遇到的主要问题:
答案 0 :(得分:3)
这里是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)
带有一个闭包() -> 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)
弗雷德·威尔玛
答案 1 :(得分:0)
不,这不可能。数组维是需要在编译时确定的,而要传递给初始化程序的参数要到运行时才知道。如果您确实想要实现这样的目标,则需要将数组索引从编译时移至运行时,例如通过索引数组访问数组。仍然没有编译验证,因为数组长度在运行时可能与数组的尺寸不匹配。
此问题类似于尝试将元组转换为数组的问题。