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

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

704056da9a4c4e075ad14479beaebab7?s=47 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

704056da9a4c4e075ad14479beaebab7?s=128

takasek

June 19, 2017
Tweet

Transcript

  1. HashableΛޡղͯ͠ ͍·ͨ͠ by. 2017/6/19 potatotips #41 1

  2. takasek iOS Developer ϑϦʔϥϯε(΄΅FiNC) @takasek OSS ActionClosurable Notifwift౳ 2

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

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

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

    { get } } public protocol Equatable { public static func ==(lhs: Self, rhs: Self) -> Bool } 5
  6. hashValue?! 6

  7. 7

  8. 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
  9. 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
  10. hashValue ͱ == Ͱ ಉ͡Α͏ͳ͜ͱ ΍ͬͯΔͳʔ! 10

  11. hashValue ʹ دͤͪΌ͑! 11

  12. 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
  13. !ɹ 13

  14. 14

  15. 15

  16. 16

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

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

  19. 19

  20. GridPoint ͸;ͨͭͷ஋ΛXORͯ͠hashValueΛٻΊ͍ͯΔ ݁Ռɺ x == y ͷͱ͖ৗʹhashValue͸0 20

  21. 21

  22. ϋογϡ஋͸ িಥͯ͠΋ྑ͍ʂ 22

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

    return lhs.hashValue == rhs.hashValue } ಉ͡ϋογϡ஋ͳΒequalͰ͋Δ͜ͱΛ ظ଴ͯ͠͠·͍ͬͯΔ࣮૷ͳͷͰɻ 23
  24. ͦ΋ͦ΋ϋογϡ஋ͱ͸ ʮσʔλ֨ೲʹ͓͍ͯ ɹઢܗ୳ࡧͩͱ͕͔͔࣌ؒΔʯ ͱ͍͏໰୊Λղܾ͢ΔͨΊɺ ϋογϡςʔϒϧͰར༻͞ΕΔ஋ 24

  25. ͲͷΑ͏ʹ σʔλ֨ೲͷ଎౓໰୊Λ ղܾ͢Δ͔ 1 1. nݸͷཁૉΛ࣋ͭ഑ྻʢ ϋ ογϡςʔϒϧ ʣΛ༻ҙ 2.

    ϋογϡ஋ % n ݸ໨ͷཁ ૉͱͯ͠σʔλΛ֨ೲ͢Δ 3. طଘͷσʔλ͕͋Δ৔߹ ͸ɺ୯ํ޲Ϧετͱͯ͠઀ ଓ͢Δʢ౳ͷํ๏ΛऔΔʣ 1 iOSΞϓϦʮΞϧΰϦζϜਤؑʯΑΓ 25
  26. ͡Ό͋ ద੾ͳhashValueͬͯ Ͳ͏࣮૷͢Ε͹ ͍͍ΜͩΖ͏! 26

  27. ޮ཰ͷྑ͍ϋογ ϡ஋ͷ࡞Γํ ʢҰൠ࿦ʣ 4 ϋογϡ஋͸ɺͳΔ΂͘σʔλͷ֨ ೲઌͱͳΔ ϋογϡςʔϒϧͷཁૉ ͕͹Β͚ΔΑ͏ͳൣғΛऔΔͱྑ͍ 4 ϋογϡςʔϒϧͷཁૉ਺n

    ͷ஋ ͸ɺ଎౓ޮ཰ vs ϝϞϦޮ཰ ͷτϨ ʔυΦϑ 4 n͕େ͖͗͢ΔͱϝϞϦΛ৯͏ ʢ֬อͨ͠ྖҬ͕࢖ΘΕͳ͍ʣ 4 n͕খ͗͢͞Δͱ଎౓͕ग़ͳ͍ ʢিಥ͠ɺઢܗ୳ࡧ͕૿͑Δʣ 27
  28. ޮ཰ͷྑ͍ϋογϡ஋ͷ࡞Γํ ʢ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
  29. ಡΜͰΘ͔Βͳ͍ͳΒ ܭଌͩ! code https://gist.github.com/takasek/ce66ac98f2971eac58991644d7471ccd 29

  30. 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
  31. 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
  32. ֦େ! 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
  33. ύϑΥʔϚϯεςετ݁Ռ ͸΍͞ ϋογϡ஋ 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
  34. var hashValue: Int { // YYYYMMDD return year * 10000

    + month * 100 + day } ૉ௚ͳ࣮૷Ͱѱ͘ͳ͍݁Ռ! 34
  35. ޮ཰ͷྑ͍ϋογϡ஋ͷ࡞Γํɾ݁࿦(࢑ఆ) ʢSwift Standard Libraryͷ৔߹ʣ ܭࢉྔ͕ଟ͘ͳΒͳ͍ఔ౓ʹɺ IntͷൣғͰͳΔ΂͘ ϢχʔΫʹͳΔ஋Λฦͤ͹Αͦ͞͏ 35

  36. ·ͱΊ ਖ਼͍͠൑அ͕Ͱ͖ΔΑ͏ʹ ΞϧΰϦζϜΛֶ΅͏ʂ 36