티스토리 뷰

Swift

Swift ) Hashable

Zedd0202 2018. 4. 10. 22:53
반응형

안녕하세요 :) Zedd입니다.

Swift 4.1변경사항 글을 쓰다가..Hashable을 제가 잘 안써서.........ㅎ.......제가..Hashable에 대해...아는건...Dictionary Key로 오려면 Hashable이어야 한다는 것 밖에...그래서 뭔가 반만 이해하는 기분..

그래서 공부 ㄱ




Hashable




뭔가 이름에서 느껴지듯이 Equatable처럼 able이 붙어있으니..프로토콜일 것 같은 느낌이죠


맞습니다..프로토콜...

정의도 간-단

"정수 Hash 값을 제공하는 타입입니다."


@_@

Overview를 보도록 합시다.


Set 또는 Dictionary의 Key로 Hashable을 준수하는 모든 타입을 사용 할 수 있습니다. 


<Dictionary 사용해보기>글에서도 언급했었죠?


표준 라이브러리의 많은 타입은 Hashable을 준수합니다.

Strings, integers, floating-point, Boolean values, sets이 기본적으로 Hash Value를 제공합니다.

(set은 Hashable한 것 만 들어갈 수 있어요)

자신만의 사용자 정의 타입도 hash가 가능할 수 있습니다. 

associated values없이 열거형(enum)을 정의하면, Hashable 준수가 자동으로 적용되고, hashValue프로퍼티를 추가하여 사용자 정의 타입에 Hashable 준수를 추가할 수 있습니다. (Swift4.1에서는 안해도 되지..)


타입의 hashValue프로퍼티에 의해 제공되는 hash 값은, 동일하게 비교되는 임의의 두 인스턴스에 대해 동일한 정수입니다.

즉, 같은 타입의 인스턴스인 a와 b의 경우, a==b이면 a.hashValue == b.hashValue입니다. 

반대의 경우는 true가 아닐 수 있습니다. 동일한 hash 값을 가진 두개의 인스턴스가 서로 동일한 필요는 없습니다.


※ 중요 : hash값은 프로그램 실행에 따라 동일하지 않을 수도 있습니다. 향후 실행에 사용할 hash값을 저장하지 마십시오.


Conforming to the Hashable Protocol



자..위에서 Set은 Hashable한 것만 들어 갈 수 있고, Dictionary의 Key역시 Hashable한다고 그랬죠?
사용자 정의 타입을 Set에 넣거나 Dictionary Key로 만들고 싶으면, 타입이 Hashable을 준수하면 됩니다.

사용자 정의 타입의 Hashable과 Equatable 요구사항은 타입이 다음 조건을 충족시킬 때 컴파일러에서 자동으로 합성?(synthesized)해줍니다. 

- 구조체의 경우, 저장프로퍼티는 모두 Hashable을 준수해야합니다.
- 열거형의 경우, 모든 associated values은 모두 Hashable을 준수해야합니다. (associated values가 없는 열거형은 정의 없이(hashValue말하는듯) Hashable을 준수합니다.)

타입의 Hashable준수를 커스터마이즈하거나, 위에서 말한 기준에 맞지 않는 형식으로 Hashable을 채택하거나, 또는 이미 존재하는 타입을 Hashable을 준수하도록 확장하는 경우에는 사용자 지정 타입에 hashValue 프로퍼티를 구현합니다. 타입이 Hashable 및 Equatable 프로토콜의 의미론적 요구사항(semantic requirements....이게 해석이 맞나)을 충족시키려면, 타입의 Equatable준수를 사용자 정의하여 일치시키는 것이 좋습니다.

예를들어, 버튼 그리드의 위치를 설명하는 GridPoint타입을 고려해보십시오. 다음은 GridPoint의 초기선언입니다.  

/// A point in an x-y coordinate system.
struct GridPoint {
    var x: Int
    var y: Int
}

.. 우리는 사용자가 탭한 grid point set 만들고 싶어요.
GridPoint타입은 아직 hash가능하지 않으므로, set element타입으로 사용할 없습니다. (위에서 말했죠?)

Hashable 준수하려면 ==연산자와 hashValue프로퍼티를 제공해야합니다.

extension GridPoint: Hashable {
    var hashValue: Int {
        return x.hashValue ^ y.hashValue &* 16777619
    }

    static func == (lhs: GridPoint, rhs: GridPoint) -> Bool {
        return lhs.x == rhs.x && lhs.y == rhs.y
    }
}

?

Swift 4.1 변경사항을 보고 오신분이라면 여기서 이상한 느낌을 받습니다.



공식문서 맞음.

아니 업뎃을 안했네 ㅡㅡ

extension GridPoint: Hashable {
    var hashValue: Int {
        return x.hashValue ^ y.hashValue &* 16777619
    }

    static func == (lhs: GridPoint, rhs: GridPoint) -> Bool {
        return lhs.x == rhs.x && lhs.y == rhs.y
    }
}

이 코드 필요없고..

struct GridPoint: Hashable {

    var x: Int

    var y: Int

}


Hashable만 채택을 해주면? ㅇㅇ 자동으로 이제 hashValue만들어줌

struct GridPoint: Hashable {

    var x: Int

    var y: Int

}


var zeddSet = Set<GridPoint>()


그럼 원래



Hashable만 element로 넣을 수 있던 Set에도 넣을 수 있고,

var zeddDic: [GridPoint:Int] = [:]


Dictionary Key로도 추가가 가능해짐 ㅇㅇ

위 이야기는 Swift 4.1변경사항 글에서 하고싶었는데, Hashable을 설명하려면 어쩔 수 없네요zz 



나는 hashValue 프로퍼티를 안만들어줬지만, 컴파일러가 자동으로 만들어줘서 있음 ㅇㅇ

근데 위처럼 컴파일러가 자동으로 만들어주는게 가능한 이유는?

/// A point in an x-y coordinate system.
struct GridPoint {
    var x: Int
    var y: Int
}

GridPoint타입의 저장프로퍼티가 모두 Hashable한 값들이니까....위에서 조건 1번 기억하시죠?

- 구조체의 경우, 저장프로퍼티는 모두 Hashable을 준수해야합니다.
- 열거형의 경우, 모든 associated values은 모두 Hashable을 준수해야합니다. (associated values가 없는 열거형은 정의 없이(hashValue말하는듯) Hashable을 준수합니다.)


아무튼 결론은...Hashable프로포콜을 사용자 정의 타입에서 채택만 하면..! 이제 Set에 들어갈 수 있는 타입도 되고, Dictionary Key로도 들어갈 수 있다..! 이겁니다..Hashable의 기능이 정말 생각한대로 이것밖에 없네요...별거없었음ㅎ


그리고 뭐 Swift4.1변경사항에서도 또 언급하겠지만.. 나는 컴파일러가.....만들어주는....이 편한....hashValue를 사용하고싶지 않네....

라고 하시면 직접 만드시면 됩니다..! 그럼 만든 hashValue를 사용하게 되니, 걱정마세용



.....

그런데 여기서 든 궁금증...왜 Set에 들어갈려면 / Dictionary Key가 되려면 왜..!! 왜 Hashable이어야 하냐??????왜??????

그렇다면 Hash값에 대해 알아야합니다.


출처 : https://ko.wikipedia.org/wiki/해시_함수


아하 검색을 빨리하기 위해..!!!

생각해보면, Swift의 Set과 Dictionary는 Array와 다르게 “순서”가 없죠? 그렇기 때문에 이 두 Collection이 Hashable과 관련이 있는 것 같아요..!

(추측임)


Hashable의 정의가 "정수 Hash 값을 제공하는 타입입니다.”라고 그랬죠? 이 정수 Hash(=hashValue)가 있기 때문에 우리가 찾으려는 원소?..를 빨리 찾을 수 있는 것이죠. 

예를들어 0 ~ 10000까지 데이터를 담을 수 있는 리스트를 생성하고, “Zedd”에 Hash함수를 적용했습니다. Hash함수에 의해 나오는 값이 바로 Hash값이라고 그랬죠? 근데 이게 음..100이 나왔어! 그럼 인덱스 100에 “Zedd”를 저장합니다. Hash함수는 동일한 인풋에는 동일한 아웃풋이 나오므로 만약에 나중에 또 “Zedd”가 들어와요 그럼 ㅇㅇ너 100으로 가셈. 그러고보니 Set과 Dictionary가 중복을 허락하지 않는 이유도 Hash때문이라고 할 수 있겠네요. 


그럼 제가 이제 “Zedd”를 찾으려고 하면 Hash함수는 또 Hash값으로 100을 리턴할테고, 나는 앞에서부터 찾을 필요 없이 걍 인덱스 100으로 가면 “Zedd”를 찾을 수 있는 것이죠. 뭐 인덱스가 충돌하고 어쩌고 그런것도 있는데, 그럴때는 해결방법이 여러가지가 있다고는 하는데..제가 배웠던건 비어있는 인덱스가 나올 때 까지 어떤 알고리즘 하나 더 돌려서 2차 Hash값을 구하는? 그런거였어요 :) 이거는 찾아보시면 될듯. 


아무튼...Hashable을 다들 잘 활용 하셨으면 좋겠어요 XD




반응형

'Swift' 카테고리의 다른 글

Swift ) FloatingPoint  (0) 2018.04.14
Swift 4.1 Released! -2  (0) 2018.04.14
Swift ) Key decoding strategy  (0) 2018.04.08
Swift 4.1 Released! - 1  (0) 2018.04.07
Swift ) NSString.CompareOptions종류  (0) 2018.03.24