Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Hashableを誤解していました / 20170619 #potatotips

takasek
June 19, 2017

Hashableを誤解していました / 20170619 #potatotips

potatotips #41 (iOS/Android開発Tips共有会) - connpass
https://potatotips.connpass.com/event/57585/
の発表資料です。

## 参考資料

Hashable - Swift Standard Library | Apple Developer Documentation
https://developer.apple.com/documentation/swift/hashable

swift/Hashing.swift at 767e8f31ac8ab8186102dc95fe5916bb2d29bbb5 · apple/swift
https://github.com/apple/swift/blob/767e8f31ac8ab8186102dc95fe5916bb2d29bbb5/validation-test/stdlib/Hashing.swift

takasek/HashablePerformanceTests.swift
https://gist.github.com/takasek/ce66ac98f2971eac58991644d7471ccd

アルゴリズム図鑑を App Store で
https://itunes.apple.com/jp/app/%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0%E5%9B%B3%E9%91%91/id1047532631?mt=8

takasek

June 19, 2017
Tweet

More Decks by takasek

Other Decks in Programming

Transcript

  1. struct YMD { let year: Int let month: Int let

    day: Int } ͜ΕΛ Set<YMD> ʹೖΕ͍ͨ 3
  2. 4

  3. public protocol Hashable : Equatable { public var hashValue: Int

    { get } } public protocol Equatable { public static func ==(lhs: Self, rhs: Self) -> Bool } 5
  4. 7

  5. A hash value, provided by a type’s hashValue property, is

    an integer that is the same for any two instances that compare equally. Πϯελϯεಉ࢜Λൺֱͯ͠equal ͳΒ ϋογϡ஋ʢ=hashValueʣ͸ಉ஋ 8
  6. struct YMD: Hashable { let year: Int let month: Int

    let day: Int var hashValue: Int { // YYYYMMDD return year * 10000 + month * 100 + day } static func ==(lhs: YMD, rhs: YMD) -> Bool { return lhs.year == rhs.year && lhs.month == rhs.month && lhs.day == rhs.day } } 9
  7. struct YMD: Hashable { let year: Int let month: Int

    let day: Int var hashValue: Int { // YYYYMMDD return year * 10000 + month * 100 + day } static func ==(lhs: YMD, rhs: YMD) -> Bool { return lhs.hashValue == rhs.hashValue } } 12
  8. 14

  9. 15

  10. 16

  11. The reverse is not true: Two instances with equal hash

    values are not necessarily equal to each other. ! equal ͳΒ ϋογϡ஋͕ಉ஋ " ϋογϡ஋͕ಉ஋ ͳΒ equal 17
  12. 19

  13. 21

  14. ͳͷͰɺ!͸Ϛζ͍ɻ static func ==(lhs: YMD, rhs: YMD) -> Bool {

    return lhs.hashValue == rhs.hashValue } ಉ͡ϋογϡ஋ͳΒequalͰ͋Δ͜ͱΛ ظ଴ͯ͠͠·͍ͬͯΔ࣮૷ͳͷͰɻ 23
  15. ͲͷΑ͏ʹ σʔλ֨ೲͷ଎౓໰୊Λ ղܾ͢Δ͔ 1 1. nݸͷཁૉΛ࣋ͭ഑ྻʢ ϋ ογϡςʔϒϧ ʣΛ༻ҙ 2.

    ϋογϡ஋ % n ݸ໨ͷཁ ૉͱͯ͠σʔλΛ֨ೲ͢Δ 3. طଘͷσʔλ͕͋Δ৔߹ ͸ɺ୯ํ޲Ϧετͱͯ͠઀ ଓ͢Δʢ౳ͷํ๏ΛऔΔʣ 1 iOSΞϓϦʮΞϧΰϦζϜਤؑʯΑΓ 25
  16. ޮ཰ͷྑ͍ϋογ ϡ஋ͷ࡞Γํ ʢҰൠ࿦ʣ 4 ϋογϡ஋͸ɺͳΔ΂͘σʔλͷ֨ ೲઌͱͳΔ ϋογϡςʔϒϧͷཁૉ ͕͹Β͚ΔΑ͏ͳൣғΛऔΔͱྑ͍ 4 ϋογϡςʔϒϧͷཁૉ਺n

    ͷ஋ ͸ɺ଎౓ޮ཰ vs ϝϞϦޮ཰ ͷτϨ ʔυΦϑ 4 n͕େ͖͗͢ΔͱϝϞϦΛ৯͏ ʢ֬อͨ͠ྖҬ͕࢖ΘΕͳ͍ʣ 4 n͕খ͗͢͞Δͱ଎౓͕ग़ͳ͍ ʢিಥ͠ɺઢܗ୳ࡧ͕૿͑Δʣ 27
  17. ޮ཰ͷྑ͍ϋογϡ஋ͷ࡞Γํ ʢSwift Standard Libraryͷ৔߹ʣ 4 ϋογϡςʔϒϧͷཁૉ਺n ͕Ͳ͏ܾఆ͞ΕΔͷ͔ɺ apple/swift ͷιʔεݟͯ΋௥͍͖Εͣ 4

    ͨͩɺجຊ hashValue ͸ Int ͷൣғ಺Ͱ޷͖ͳ஋Λฦͤ ͹Α͠ͳʹ _squeezeHashValue ͯ͘͠ΕͯΔͬΆ͍ 4 ৄࡉͳϩδοΫ͸ཧղͯ͠ͳ͍͚Ͳɺ2ͷႈ৐Λ࢖ͬͯ hashValueͷিಥΛݮΒ͢ϩδοΫΒ͍͠ 4 cf. https://github.com/apple/swift/blob/master/ stdlib/public/core/Hashing.swift 28
  18. func YMDΛ10೥෼SetʹಥͬࠐΉ(using key: YMD.Key) { let cal = Calendar(identifier: .gregorian)

    // 2000/1/1 ͔Β 2010/12/31 ·Ͱͷ೔෇͕ॲཧର৅ let firstDate = DateComponents(calendar: cal, year: 2000, month: 1, day: 1).date! let lastDate = DateComponents(calendar: cal, year: 2010, month: 12, day: 31).date! var ymds: Set<YMD> = [] var cursor = firstDate while cursor < lastDate { //Χʔιϧ͕ࣔ͢YMDΛSetʹೖΕΔ ymds.insert(YMD( year: cal.component(.year, from: cursor), month: cal.component(.month, from: cursor), day: cal.component(.day, from: cursor), keyForHashValue: key )) //ΧʔιϧΛҰ೔ਐΊΔ cursor = cal.date(byAdding: .day, value: 1, to: cursor)! } } 30
  19. struct YMD: Hashable { let year: Int let month: Int

    let day: Int enum Key { case year, month, day, plusAll, xorAll, shiftAll, none } let keyForHashValue: Key var hashValue: Int { switch keyForHashValue { case .year: return year case .month: return month case .day: return day case .plusAll: return year * 10000 + month * 100 + day case .xorAll: return year ^ month ^ day case .shiftAll: return year << 14 + month << 5 + day //Ϗοτγϑτͯ͠ϢχʔΫʹ case .none: return 0 } } static func ==(lhs: YMD, rhs: YMD) -> Bool { return lhs.year == rhs.year && lhs.month == rhs.month && lhs.day == rhs.day } } 31
  20. ֦େ! var hashValue: Int { switch keyForHashValue { case .year:

    return year case .month: return month case .day: return day case .plusAll: return year * 10000 + month * 100 + day case .xorAll: return year ^ month ^ day case .shiftAll: return year << 14 + month << 5 + day case .none: return 0 } } 32
  21. ύϑΥʔϚϯεςετ݁Ռ ͸΍͞ ϋογϡ஋ 10೥෼SetʹಥͬࠐΉ࣌ؒ !1 YYYYMMDD 0.035 sec 2 Ϗοτγϑτ

    0.037 sec 3 day 0.193 sec 4 Y ^ M ^ D 0.241 sec 5 month 0.335 sec 6 year 0.858 sec "7 ৗʹ0 2.656 sec 33
  22. var hashValue: Int { // YYYYMMDD return year * 10000

    + month * 100 + day } ૉ௚ͳ࣮૷Ͱѱ͘ͳ͍݁Ռ! 34