在Swift中,我可以使用元组作为字典中的键吗?

JuJ*_*oDi 51 dictionary tuples swift

我想知道我是否能以某种方式使用x,y对作为我字典的关键

let activeSquares = Dictionary <(x: Int, y: Int), SKShapeNode>()
Run Code Online (Sandbox Code Playgroud)

但我得到错误:

Cannot convert the expression's type '<<error type>>' to type '$T1'
Run Code Online (Sandbox Code Playgroud)

和错误:

Type '(x: Int, y: Int)?' does not conform to protocol 'Hashable'
Run Code Online (Sandbox Code Playgroud)

那么..我们怎样才能使它符合要求?

Luk*_*kas 41

Dictionaryis 的定义struct Dictionary<KeyType : Hashable, ValueType> : ...,即密钥的类型必须符合协议Hashable.但语言指南告诉我们,协议可以通过类,结构和枚举来采用,即不是通过元组.因此,元组不能用作Dictionary键.

一种解决方法是定义一个包含两个Int(或者你想要放在元组中的任何内容)的hashable结构类型.

  • 哇,这似乎完全错过了可能是一个很棒的语言功能.我的意思是我没有理由认为他们不能通过组合其组件的哈希来使元组隐含地可以清除.避免二维词典本来是一种非常好的方法! (16认同)
  • 假设我在两个Int属性中创建了一个唯一的hashable结构.我应该如何将两个整数的组合映射到一个唯一的哈希?"x ^ y"?"x << 16 | y"? (3认同)
  • 完全应该是一个功能,如果更高版本的Swift没有添加它,我会感到惊讶(虽然v4仍然没有它).在Python中,一个hashables元组是自动可以清除的. (2认同)
  • 该提案已被接受:https://github.com/apple/swift-evolution/blob/master/proposals/0283-tuples-are-equatable-comparable-hashable.md (2认同)

Mar*_*gor 18

如上面的答案所述,这是不可能的.但是您可以使用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"
Run Code Online (Sandbox Code Playgroud)

  • 你能解释`&*31&+'部分的作用吗? (4认同)
  • &*和&+类似于正常操作*和+但具有溢出错误保护(因此在溢出的情况下不会引发错误) (3认同)
  • 在Swift 3中,`==`已被移动到结构内的静态函数:`static func == <T:Hashable,U:Hashable>(...) - > Bool {}` (2认同)

nor*_*DEV 7

不需要特殊的代码或幻数来实现 Hashable

Swift 4.2中的 Hashable :

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
    }
}
Run Code Online (Sandbox Code Playgroud)

更多信息: https: //nshipster.com/hashable/


小智 5

我在一个应用程序中创建了以下代码:

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)]!
Run Code Online (Sandbox Code Playgroud)


小智 5

如果您不介意效率低下,则可以轻松地将元组转换为字符串,然后将其用作字典键...

var dict = Dictionary<String, SKShapeNode>() 

let tup = (3,4)
let key:String = "\(tup)"
dict[key] = ...
Run Code Online (Sandbox Code Playgroud)


Max*_*tov 5

不幸的是,从 Swift 4.2 开始,标准库仍然没有Hashable为元组提供条件一致性,编译器认为这不是有效代码:

extension (T1, T2): Hashable where T1: Hashable, T2: Hashable {
  // potential generic `Hashable` implementation here..
}
Run Code Online (Sandbox Code Playgroud)

此外,将元组作为其字段的结构、类和枚举将不会得到 Hashable自动合成。

虽然其他答案建议使用数组而不是元组,但这会导致效率低下。元组是一种非常简单的结构,由于元素的数量和类型在编译时已知,因此可以轻松优化。一个Array实例几乎总是预先分配更多的连续内存以适应要添加的潜在元素。此外,使用Array类型强制您要么使项目类型相同,要么使用类型擦除。也就是说,如果你不关心低效率(Int, Int)可以存储在[Int],但(String, Int)需要类似的东西[Any].

我发现的解决方法依赖于Hashable为单独存储的字段自动合成这一事实,因此即使没有像Marek Gregor 的回答那样手动添加HashableEquatable实现,此代码也能工作:

struct Pair<T: Hashable, U: Hashable>: Hashable {
  let first: T
  let second: U
}
Run Code Online (Sandbox Code Playgroud)