2014-06-10 67 views
41

我想知道如果我能以某種方式使用x,y對爲重點,以我的字典在Swift中,我可以使用元組作爲字典中的鍵嗎?

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

+1。作爲值的元組是否工作? – Thilo

+3

我相信'(x:Int,y:Int)'不是一個哈希值的東西:) –

+0

@Thilo是元組作爲值工作 – JuJoDi

回答

29

Dictionary的定義是struct Dictionary<KeyType : Hashable, ValueType> : ...,即密鑰的類型必須符合協議Hashable。但語言指南告訴我們protocols can be adopted by classes, structs and enums,即不是元組。因此,元組不能用作Dictionary鍵。

解決方法是定義一個包含兩個Ints(或任何你想放在你的元組中)的可哈希結構類型。

+3

假設我在兩個Int屬性中使可哈希結構唯一。我應該如何將兩個整數的組合映射到一個唯一的散列? 「x^y」? 「x << 16 | y」? – ccwasden

+11

Aww,這似乎是這樣一個完全錯過了什麼可能是一個很棒的語言功能。我的意思是我沒有理由認爲他們不可能通過組合它們的組件的哈希來使得元組隱含地哈希。這將是避免二維字典的一個非常快速的方法! – devios1

+1

完全應該是一個功能,如果以後版本的Swift沒有添加它(雖然v4仍然沒有它),我會感到驚訝。在Python中,一個hashables元組可自動散列。 – sudo

17

正如上面的答案所述,這是不可能的。但是你可以用解析成通用結構與哈希的協議作爲一種解決方法:

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" 
+1

你能解釋一下'&* 31&+'部件的作用嗎? – devios1

+2

&*和&+就像正常的操作*和+但具有溢出錯誤保護(所以在溢出的情況下不會引發錯誤) –

+2

在Swift 3中,'=='已經被移動到struct: static func == (...) - > Bool {}' – BallpointBen

2

我創造了這個代碼的應用程序:

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)]! 
+0

不錯的例子 – eonist

1

如果你不介意有點低效率的,你可以輕鬆地將您的元組轉換爲字符串,然後使用該字典鍵...

var dict = Dictionary<String, SKShapeNode>() 

let tup = (3,4) 
let key:String = "\(tup)" 
dict[key] = ... 
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 
} 

它然後使用字符串的哈希值快得多。

如果你想知道更多關於。

相關問題