[英]N-Dimensional array swift
Is there any way to have an n dimensional array in swift? 有没有办法在swift中有一个n维数组? I would like to be able to make a function that creates an array with n dimensions but I cannot figure out how. 我希望能够创建一个创建一个n维数组的函数,但我无法弄清楚如何。
Basically something like this: 基本上是这样的:
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
}
The above code does not work for obvios reasons but, I think it points out the main problems I am having: 以上代码不适用于obvios原因,但我认为它指出了我遇到的主要问题:
Here is the implementation of an N-Dimensional Array. 这是N维数组的实现。 It uses a normal array internally for storage and converts the multi-dimensional indices into a single index for the internal array. 它在内部使用普通数组进行存储,并将多维索引转换为内部数组的单个索引。
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
}
}
}
Example: 例:
// 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)"] [“(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
Initializing an Array with a Reference Type 使用引用类型初始化数组
As @DuncanC noted in the comments, you have to be careful when initializing an array with a value which is a reference type, because the array will be filled with references to the object and modifying the object at any index will modify all of them. 正如@DuncanC在评论中指出的那样,在初始化具有引用类型值的数组时必须小心,因为数组将填充对对象的引用,并且修改任何索引处的对象将修改所有这些对象。
To solve this, I added a second initializer: 为了解决这个问题,我添加了第二个初始化器:
init(dimensions: Int..., initUsing initializer: () -> T)
which takes a closure () -> T
which can be used to create a new object for each element of the array. 它接受一个closure () -> T
,它可以用来为数组的每个元素创建一个新对象。
For example: 例如:
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)
Fred Wilma 弗雷德威尔玛
Nope, it's not possible. 不,这是不可能的。 Array dimensions is something that needs to be determined at compile time, while the argument you want to pass to the initializer will not be known until runtime. 数组维度需要在编译时确定,而要传递给初始值设定项的参数在运行时才会知道。 If you really want to achieve something like this, then you'll need to move the array indexing from compile time to runtime, eg by accessing the array via an array of indexes. 如果你真的想要实现这样的东西,那么你需要将数组索引从编译时移动到运行时,例如通过索引数组访问数组。 Still you don't have compile validation, since the array length can at runtime to not match the dimensions of the array. 您仍然没有编译验证,因为数组长度在运行时可能与数组的维度不匹配。
This problem is similar to the one that attempts to convert a tuple to an array. 此问题类似于尝试将元组转换为数组的问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.