使用变量作为索引的Swift Tuple索引? 任何人都知道是否可以使用变量作为Swift元组索引的索引。我希望使用随机数从元组中选择和项目。我有随机数作为变量,但无法看到如何使用它作为元组的索引。我已经搜索了各个地方
答案 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;并回答 - 你不能