使用变量作为索引的Swift Tuple索引?

时间:2016-06-28 11:29:35

标签: swift variables indexing tuples

使用变量作为索引的Swift Tuple索引? 任何人都知道是否可以使用变量作为Swift元组索引的索引。我希望使用随机数从元组中选择和项目。我有随机数作为变量,但无法看到如何使用它作为元组的索引。我已经搜索了各个地方

5 个答案:

答案 0 :(得分:6)

确保您选择了正确的数据结构

如果您已经达到了需要访问元组成员的点,就像“索引”一样,您应该查看数据结构,看看在这种情况下元组是否真的是您的正确选择。正如MartinR在评论中提到的那样,也许一个数组会是一个更合适的选择,或者就像simpleBob提到的那样,是一个字典。

从技术上讲:是的,可以通过索引式

访问元组成员

您可以利用运行时内省来访问元组的镜像表示的子元素,并在给定索引的情况下选择子值。

,例如,对于所有元素属于同一类型的5元组:

func getMemberOfFiveTuple<T>(tuple: (T, T, T, T, T), atIndex: Int) -> T? {
    let children = Mirror(reflecting: tup).children
    guard case let idx = IntMax(atIndex)
        where idx < children.count else { return nil }

    return children[children.startIndex.advancedBy(idx)].value as? T
}

let tup = (10, 11, 12, 13, 14)
let idx = 3

if let member = getMemberOfFiveTuple(tup, atIndex: idx) {
    print(member)
} // 13

请注意child.value的类型是Any,因此我们需要尝试将类型转换回元组的元素类型。如果您的元组包含不同类型的成员,则在编译时无法知道返回类型,并且您可能必须继续对返回的元素使用类型Any

答案 1 :(得分:1)

所以,这里的人们说 - 如果你实际上不需要一个元组,就不要使用元组 - 是正确的。但是,有一点需要注意。

根据我的经验,在性能关键代码中使用具有静态大小的Swift数组实际上会使代码速度降低很多。看看:

let clz_lookup = [4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
func clz(x : UInt32) -> Int {
    guard x != 0 else { return 32 }
    var x = x
    var n = 0

    if (x & 0xFFFF0000) == 0 { n  = 16; x <<= 16 } else { n = 0 }
    if (x & 0xFF000000) == 0 { n +=  8; x <<=  8 }
    if (x & 0xF0000000) == 0 { n +=  4; x <<=  4 }

    return n + clz_lookup[Int(x >> (32 - 4))]
}

这是一段相当简单的代码,最后使用了缓存的查找表。但是,如果我们使用工具对其进行分析,我们会发现查找实际上比它替换的if语句慢得多!我的猜测是这可能是边界检查或类似的原因,加上在实现中有一个额外的指针间接。

在任何情况下,这都不好,因为上面是一个可能被大量调用的函数,我们想要优化它。一个简单的技术是创建一个元组而不是一个数组,然后简单地使用withUnsafeMutablePointer直接获取指向元组的指针(它在内存中作为一个连续的静态数组布局 - 正是我们想要的这种情况!):

var clz_table = (4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0)
let clz_lookup = withUnsafeMutablePointer(to: &clz_table) { $0.withMemoryRebound(to: Int.self, capacity: 15) { $0 } }

这可以像普通的C风格数组一样使用,因为你可以索引它,甚至可以改变特定索引的值,但是没有办法增长这个数组。这种索引方法应该比提到反射的其他解决方案快得多,但如果使用错误则可能不安全。

答案 2 :(得分:0)

参考:https://gist.github.com/ctreffs/785db636d68a211b25c989644b13f301

在Swift 5中:

func tupleToArray<Tuple, Value>(tuple: Tuple) -> [Value] {
    let tupleMirror = Mirror(reflecting: tuple)
    assert(tupleMirror.displayStyle == .tuple, "Given argument is no tuple")
    assert(tupleMirror.superclassMirror == nil, "Given tuple argument must not have a superclass (is: \(tupleMirror.superclassMirror!)")
    assert(!tupleMirror.children.isEmpty, "Given tuple argument has no value elements")
    return tupleMirror.children.compactMap { (child: Mirror.Child) -> Value? in
        let valueMirror = Mirror(reflecting: child.value)
        assert(valueMirror.subjectType == Value.self, "Given tuple argument's child type (\(valueMirror.subjectType)) does not reflect expected return value type (\(Value.self))")
        return child.value as? Value
    }
}

let sss: [String] = tupleToArray(tuple: ("?", "??", "✈️✈️✈️" ,"???"))
print(sss)

答案 3 :(得分:0)

只有当元组具有同构类型时,您才能将元组转换为缓冲区。 但在这样做之前,想知道数组是否不能完成这项工作。

var tuple: (Int, Int, Int, Int) = (0,1,2,3)

withUnsafeMutablePointer(to: &tuple) { pointer in
    pointer.withMemoryRebound(to: Int.self, capacity: 4) { buffer in
        buffer[3] = 0
    }
}

print(tuple)

结果:

(0, 1, 2, 0)

答案 4 :(得分:-2)

只要元组索引只是默认名称,您就可以提供自己的名称

let testTuple = (first: 1, second: 2.0)
let first: Int = testTuple.first
let second: Double = testTuple.second

您不能将变量用作索引。非常接近的问题&#34;我可以使用KVC作为元组吗?&#34;并回答 - 你不能