Swift 新特性 Hasher

Swift 新特性 Hasher

Previously on the Hashable

Swift 3年多了。

近期Xcode从10.1更新到10.2,支持Swift 5了,

升级自己工程的时候,除了switch语句要增加对未知枚举的处理外,

还发现HashablehashValue被弃用了,取而代之的是

func hash(into hasher: inout Hasher)

这是个啥?直译过来是散列器,一起来看看吧。

Deprecation & New

public protocol Hashable : Equatable {

    // 被弃用的hashValue。
    var hashValue: Int { get }

    // 现在遵循Hashable需要实现这个方法。
    func hash(into hasher: inout Hasher)
    
}

func hash(into hasher: inout Hasher)的原文注释

 Hashes the essential components of this value by feeding them into the given hasher.

 Implement this method to conform to the `Hashable` protocol. 
 The components used for hashing must be the same as 
 the components compared in your type's `==` operator implementation. 
 Call `hasher.combine(_:)` with each of these components.

 - Important: Never call `finalize()` on `hasher`. Doing so may become a
   compile-time error in the future.

 - Parameter hasher: The hasher to use when combining the components
   of this instance.

结合自己的理解,翻译过来就是

将这个instance的必要成分填充至给定的散列器,来散列它。

实现这个方法来遵循Hashable协议。

这些必要成分必须和以前实现的==方法里面的成分一样。

  • 要点:不要调用Hasherfinalize() 方法,未来可能会造成编译错误。
  • 参数haser: 主要是用来组合这个实例的成分。

Hasher

 The universal hash function used by `Set` and `Dictionary`.

 `Hasher` can be used to map an arbitrary sequence of bytes to an integer
 hash value. You can feed data to the hasher using a series of calls to
 mutating `combine` methods. When you've finished feeding the hasher, the
 hash value can be retrieved by calling `finalize()`:

     var hasher = Hasher()
     hasher.combine(23)
     hasher.combine("Hello")
     let hashValue = hasher.finalize()

 Within the execution of a Swift program, `Hasher` guarantees that finalizing
 it will always produce the same hash value as long as it is fed the exact
 same sequence of bytes. However, the underlying hash algorithm is designed
 to exhibit avalanche effects: slight changes to the seed or the input byte
 sequence will typically produce drastic changes in the generated hash value.

 - Note: Do not save or otherwise reuse hash values across executions of your
   program. `Hasher` is usually randomly seeded, which means it will return
   different values on every new execution of your program. The hash
   algorithm implemented by `Hasher` may itself change between any two
   versions of the standard library.

Hasher,通用的哈希函数,被SetDictionary所采用。

Hasher,可以将一段任意字节数据映射为integer哈希值。

你可以调用多次combine方法来填充hasher(该方法是可以修改hasher的,所以用mutating修饰),

在这之后,通过finalize()可以取得hash value

代码示例。

Swift程序的执行过程中,只要填充了相同的数据,

Hasher就能确保finalize()始终能得到相同的哈希值,

但是呢,设计的底层哈希算法拥有这样的特性:

种子数据发生轻微的变化,会造成巨大的哈希值变化。

(PS: 以上这两点可以提高查找的效率,减少hash value冲突,也就减少调用==方法)

需要注意的是,不要保存或重用程序执行中得到的hash value

Hasher会经常随机化种子,意味着每次程序执行的时候,Hasher的哈希算法可能会变。

public struct Hasher {

 Creates a new hasher.

 The hasher uses a per-execution seed value that is set during process
 startup, usually from a high-quality random source.
 
 创建一个新的hasher,它会使用高质量的随机种子,每次进程开启,这些种子都会重新生成。
 
    public init()
 

 Adds the given value to this hasher, mixing its essential parts into the
 hasher state.

 Parameter value: A value to add to the hasher.
 
 添加已遵循Hashable的值到Hasher中,这些值是构成哈希的重要部分。
 
    @inlinable public mutating func combine<H>(_ value: H) where H : Hashable


 Adds the contents of the given buffer to this hasher, mixing it into the
 hasher state.

 Parameter bytes: A raw memory buffer.
 
 添加字节数组到Hasher中。
 
    public mutating func combine(bytes: UnsafeRawBufferPointer)


 Finalizes the hasher state and returns the hash value.

 Finalizing consumes the hasher: it is illegal to finalize a hasher you
 don't own, or to perform operations on a finalized hasher. (These may
 become compile-time errors in the future.)

 Hash values are not guaranteed to be equal across different executions of
 your program. Do not save hash values to use during a future execution.

 Returns: The hash value calculated by the hasher.
 
 终结这个hasher的状态,获得hash value。
 
 不是你生成的haser,调用finalize是非法的。
 
 已经调用过finalize的hasher也不能再调用finalize了。未来可能会引起编译错误。
 
 每次程序执行不能保证得到相同的哈希值。因此不要持久化它。
 
    public __consuming func finalize() -> Int
    
}

Why Hasher?

结合自己的理解,Hasher有以下优点:

  1. 简化开发者遵循Hashable的工作量。开发者不需要关心hashValue怎么计算的。
  2. 一定程度上降低了SwifthashValue上的冲突。基于文档上所说的底层哈希算法
  3. 扩展了可哈希的范围。除了已遵循Hashable的类型,还支持字节数组

Seems like Zobrist Hash

中国象棋的实现中,哈希启发我用到了Zobrist Hash

过程是这样的,

  • 对于每个棋子,每个棋盘位置,均预先生成随机数。每次程序运行的时候,随机数总是新生成的。

  • 对于每个局面,由棋子和棋子的位置构成。

  • 局面的key,可以表示为,棋子在其位置上的随机数异或和。

有以下优点:

  1. 顺序无关性。A ^ B = B ^ A。
  2. 差异性。若 B ≠ C,则 A ^ B ≠ A ^ C。
  3. 快速删除、添加。若 Key = Value ^ A,那么移除A的操作是 Key ^= A,其他操作同理。

Conclusion

遵循Hashable的步骤:

  1. 声明:func hash(into hasher: inout Hasher)
  2. 实现:连续调用hasher.combine(targetValue)

注意事项:

  1. 不要调用你没生成的Hasherfinalize方法。
  2. 尽量不要持久化hash value,即便在同一个程序执行生命周期内。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,383评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,522评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,852评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,621评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,741评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,929评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,076评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,803评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,265评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,582评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,716评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,395评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,039评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,027评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,488评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,612评论 2 350

推荐阅读更多精彩内容