简体   繁体   English

N维数组swift

[英]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原因,但我认为它指出了我遇到的主要问题:

  • How do I define a return type 如何定义返回类型
  • How do I actually create the array 我如何实际创建数组
  • Once created how do I traverse and populate the array 一旦创建,我如何遍历和填充数组

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.

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