我想知道我是否可以某种方式使用x,y对作为我词典的关键
let activeSquares = Dictionary <(x: Int, y: Int), SKShapeNode>()
但我收到错误:
Cannot convert the expression's type '<<error type>>' to type '$T1'
和错误:
Type '(x: Int, y: Int)?' does not conform to protocol 'Hashable'
那么..我们怎样才能使它符合要求?
答案 0 :(得分:38)
Dictionary
的定义是struct Dictionary<KeyType : Hashable, ValueType> : ...
,即密钥的类型必须符合协议Hashable
。但语言指南告诉我们protocols can be adopted by classes, structs and enums,即不是元组。因此,元组不能用作Dictionary
个键。
一种解决方法是定义一个包含两个Ints的哈希结构类型(或者你想要放在元组中的任何内容)。
答案 1 :(得分:19)
如上面的答案所述,这是不可能的。但是你可以使用Hashable协议将元组包装成通用结构作为解决方法:
struct Two<T:Hashable,U:Hashable> : Hashable {
let values : (T, U)
var hashValue : Int {
get {
let (a,b) = values
return a.hashValue &* 31 &+ b.hashValue
}
}
}
// comparison function for conforming to Equatable protocol
func ==<T:Hashable,U:Hashable>(lhs: Two<T,U>, rhs: Two<T,U>) -> Bool {
return lhs.values == rhs.values
}
// usage:
let pair = Two(values:("C","D"))
var pairMap = Dictionary<Two<String,String>,String>()
pairMap[pair] = "A"
答案 2 :(得分:5)
我在app中创建了这段代码:
struct Point2D: Hashable{
var x : CGFloat = 0.0
var y : CGFloat = 0.0
var hashValue: Int {
return "(\(x),\(y))".hashValue
}
static func == (lhs: Point2D, rhs: Point2D) -> Bool {
return lhs.x == rhs.x && lhs.y == rhs.y
}
}
struct Point3D: Hashable{
var x : CGFloat = 0.0
var y : CGFloat = 0.0
var z : CGFloat = 0.0
var hashValue: Int {
return "(\(x),\(y),\(z))".hashValue
}
static func == (lhs: Point3D, rhs: Point3D) -> Bool {
return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z
}
}
var map : [Point2D : Point3D] = [:]
map.updateValue(Point3D(x: 10.0, y: 20.0,z:0), forKey: Point2D(x: 10.0,
y: 20.0))
let p = map[Point2D(x: 10.0, y: 20.0)]!
答案 3 :(得分:4)
如果您不介意一些低效率,您可以轻松地将您的元组转换为字符串,然后将其用于字典键......
var dict = Dictionary<String, SKShapeNode>()
let tup = (3,4)
let key:String = "\(tup)"
dict[key] = ...
答案 4 :(得分:1)
我建议实现类似于boost::hash_combine
的结构和使用解决方案。
以下是我使用的内容:
struct Point2: Hashable {
var x:Double
var y:Double
public var hashValue: Int {
var seed = UInt(0)
hash_combine(seed: &seed, value: UInt(bitPattern: x.hashValue))
hash_combine(seed: &seed, value: UInt(bitPattern: y.hashValue))
return Int(bitPattern: seed)
}
static func ==(lhs: Point2, rhs: Point2) -> Bool {
return lhs.x == rhs.x && lhs.y == rhs.y
}
}
func hash_combine(seed: inout UInt, value: UInt) {
let tmp = value &+ 0x9e3779b97f4a7c15 &+ (seed << 6) &+ (seed >> 2)
seed ^= tmp
}
比使用字符串作为哈希值要快得多。
如果您想了解有关magic number的更多信息。
答案 5 :(得分:1)
不幸的是,从Swift 4.2开始,标准库仍然没有为元组提供符合Hashable
的条件,并且编译器认为这不是有效的代码:
extension (T1, T2): Hashable where T1: Hashable, T2: Hashable {
// potential generic `Hashable` implementation here..
}
此外,以元组为字段的结构,类和枚举不会自动合成Hashable
。
虽然其他答案建议使用数组而不是元组,但这会导致效率低下。元组是一种非常简单的结构,由于在编译时就知道元素的数量和类型,因此可以很容易地对其进行优化。 Array
实例几乎总是preallocates more contiguous memory,以适应可能添加的元素。此外,使用Array
类型会强制您使元组类型相同或使用类型擦除。也就是说,如果您不关心效率低下的问题,可以将(Int, Int)
存储在[Int]
中,但是(String, Int)
将需要类似[Any]
的内容。
我发现的解决方法取决于Hashable
会自动为单独存储的字段自动合成的事实,因此即使没有手动添加{{3}的Hashable
和Equatable
实现,此代码也能正常工作}}:
struct Pair<T: Hashable, U: Hashable>: Hashable {
let first: T
let second: U
}
答案 6 :(得分:1)
无需特殊代码或魔术数字即可实现Hashable
可哈希于 Swift 4.2 :
<IfModule mod_rewrite.c>
<FilesMatch '\.(pdf|mp4)'>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_URI} !.*\/sites\/[0-9]+\/.*
RewriteCond %{REQUEST_URI} ^.*/[0-9]{4}/[0-9]{2}.*$
RewriteRule ^([^?]*)$ /index.php?uamfiletype=attachment&uamgetfile=$1 [QSA,L]
RewriteRule ^(.*)\?(((?!uamfiletype).)*)$ /index.php?uamfiletype=attachment&uamgetfile=$1&$2 [QSA,L]
RewriteRule ^(.*)\?(.*)$ /index.php?uamgetfile=$1&$2 [QSA,L]
</FilesMatch>
</IfModule>
答案 7 :(得分:1)
将扩展文件添加到项目(View on gist.github.com):
extension Dictionary where Key == Int64, Value == SKNode {
func int64key(_ key: (Int32, Int32)) -> Int64 {
return (Int64(key.0) << 32) | Int64(key.1)
}
subscript(_ key: (Int32, Int32)) -> SKNode? {
get {
return self[int64key(key)]
}
set(newValue) {
self[int64key(key)] = newValue
}
}
}
声明:
var dictionary: [Int64 : SKNode] = [:]
使用:
var dictionary: [Int64 : SKNode] = [:]
dictionary[(0,1)] = SKNode()
dictionary[(1,0)] = SKNode()
答案 8 :(得分:0)
或者只是使用数组代替。我正在尝试执行以下代码:
let parsed:Dictionary<(Duration, Duration), [ValveSpan]> = Dictionary(grouping: cut) { span in (span.begin, span.end) }
是哪一位带我到这则帖子的。仔细阅读这些内容并感到失望之后(因为如果他们可以通过采用协议而合成Equatable
和Hashable
而无所事事,那么他们应该能够为元组做到这一点,不是吗?),我突然意识到,则只需使用数组即可。不知道它有多有效,但是这种更改可以正常工作:
let parsed:Dictionary<[Duration], [ValveSpan]> = Dictionary(grouping: cut) { span in [span.begin, span.end] }
我更笼统的问题是“那么为什么不像数组那样元组第一类结构呢?Python将其删除(运行)。”
答案 9 :(得分:0)
您还不能在 Array
中,但您可以使用 var dictionary: Dictionary<[Int], Any> = [:]
代替元组:
dictionary[[1,2]] = "hi"
dictionary[[2,2]] = "bye"
而且用法很简单:
dictionary[[1,2,3,4,5,6]] = "Interstellar"
它还支持任何维度:
{{1}}