どういうわけか、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'
それで..どうやって適合させることができますか?
Dictionary
の定義はstruct Dictionary<KeyType : Hashable, ValueType> : ...
、つまりキーのタイプはプロトコルHashable
に準拠する必要があります。しかし、言語ガイドには、 プロトコルはクラス、構造体、および列挙型で採用できる 、つまりタプルではないことが示されています。したがって、タプルをDictionary
キーとして使用することはできません。
回避策は、2つのInt(またはTupleに入れたいもの)を含むハッシュ可能な構造体型を定義することです。
上記の答えで述べたように、それは不可能です。ただし、回避策としてHashableプロトコルを使用してTupleを汎用構造にラップできます。
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"
アプリでこのコードを作成しました:
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)]!
非効率性を少しでも気にしない場合は、タプルを文字列に簡単に変換して、辞書キーに使用できます...
var dict = Dictionary<String, SKShapeNode>()
let tup = (3,4)
let key:String = "\(tup)"
dict[key] = ...
残念ながら、Swift 4.2の時点で、標準ライブラリはタプルのHashable
への条件付き適合をまだ提供しておらず、これはコンパイラーによって有効なコードと見なされません。
extension (T1, T2): Hashable where T1: Hashable, T2: Hashable {
// potential generic `Hashable` implementation here..
}
さらに、フィールドとしてタプルを持つ構造体、クラス、および列挙型はHashable
が自動的に合成されません。
他の答えはタプルの代わりに配列を使用することを提案しましたが、これは非効率を引き起こします。タプルは非常に単純な構造で、コンパイル時に要素の数と型がわかっているため、簡単に最適化できます。 Array
インスタンスは、ほとんど常に 追加の潜在的な要素に対応するために、より連続したメモリを事前に割り当てます 。また、Array
型を使用すると、タプル型を同じにするか、型消去を使用する必要があります。つまり、非効率性を気にしない場合は、(Int, Int)
を[Int]
に格納できますが、(String, Int)
は[Any]
のようなものを必要とします。
私が見つけた回避策は、Hashable
が個別に格納されたフィールドに対して自動的に合成するという事実に依存しているため、このコードは Marek Gregor's answer のようなHashable
およびEquatable
実装を手動で追加しなくても機能します:
struct Pair<T: Hashable, U: Hashable>: Hashable {
let first: T
let second: U
}
Hashableの実装に特別なコードやマジックナンバーは不要
ハッシュ可能Swift 4.2:
struct PairKey: Hashable {
let first: UInt
let second: UInt
func hash(into hasher: inout Hasher) {
hasher.combine(self.first)
hasher.combine(self.second)
}
static func ==(lhs: PairKey, rhs: PairKey) -> Bool {
return lhs.first == rhs.first && lhs.second == rhs.second
}
}