2015년 2월 25일 수요일

Swift 1.2 - Set 타입

Swift 1.2 에서 'Set' 이라는 새로 추가된 컬렉션 타입이 있다. Set 타입은 중복되지 않는 값들을 보관하기 위한 순서가 없는 배열과 비슷하다. 혹은 수학에서 흔히 말하는 '집합'과도 비슷하다.이 Set 타입에 관해서 간단히 정리해 보려고 한다.

일반적인 이용법

Set 타입은 아래와 같은 몇 가지 방식으로 인스턴스화 할 수 있다.
let someSet = Set([1, 2, 3, 4, 5])
let someStringSet: Set<String> = ["a", "bb", "ccc"]
다만, AnyObject 타입은 가질 수가 없다.
var anySet = Set<AnyObject>()
// Type 'AnyObject' does not conform to protocol 'Hashable'
명시적으로 '확실한 타입'을 가져야 한다는 제약이 있다. 이 외에는 일반적인 컬렉션 타입과 비슷하다.

컬렉션타입이니 만큼, 많은 데이터를 보관 할 수 있으며 (상수가 아닌 한) 값을 마음대로 추가/삭제 할 수 있다.
var oddNumbers = Set<Int>()

oddNumbers.isEmpty
// true

oddNumbers.insert(1)
oddNumbers.insert(3)
oddNumbers.insert(1)    // returns 'nil'
oddNumbers  // {3, 1}

oddNumbers.insert(5)
oddNumbers.insert(7)
oddNumbers  // {5, 7, 3, 1}
insert 메소드를 이용해 값을 마음껏 추가 할 수 있다. 물론 당연하겠지만 isEmpty 프로퍼티로 비어있는지 여부도 확인 가능하다.

앞서 이야기 한 대로, Set 타입은 중복되는 데이터를 가질 수 없다. 하지만 중복되는 값을 넣는다고 오류가 나는 것은 아니다. 위 예제의 경우 '1'을 두 번 넣었는데, 두 번째 삽입 과정에서 그냥 nil이 리턴된다. 문제가 없다면 원래 수치와 동일한 값이 리턴된다는 점을 알아두자.

그리고 Set은 순서를 가지지 않는다고 이야기 한 점이 잘 보일 것이다. 실제로 값이 표시되는 순서는 알 수가 없는데, 순서는 해싱(Hashing)함수의 결과에 따른 것이라고 생각된다. 물론 추측일 뿐이지만, 보관할 수 있는 타입이 Hashable 제약을 가진다는 점을 볼 때 매우 신빙성이 높다. :-)

삽입을 했으니 삭제도 가능한 건 당연할 것이다.
oddNumbers.insert(8)
oddNumbers  // {5, 7, 3, 1, 8}
oddNumbers.remove(8)
oddNumbers  // {5, 7, 3, 1}

oddNumbers.remove(10)      // returns 'nil'
remove 메소드로 삭제가 가능하다. 없는 값을 삭제할 경우 'nil'이 리턴된다.

집합스러운 연산

수학시간에 집한 연산을 하면서 교집합이니 차집합이니 이런 식의 단어를 많이 들었을 것이다. Set 에도 이와 비슷한 기능들이 제공된다.

우선 겹치는 것들이 있는지 파악해보자.
var evenNumbers = Set([2, 4, 6, 8, 10])

oddNumbers.isDisjointWith(evenNumbers)
// true

oddNumbers.isDisjointWith(Set([1, 2, 3, 5]))
// false
isDisjointWith 메소드를 활용해 Set 끼리 데이터가 겹치는지 파악이 가능하다. 만약 겹치는 아이템이 하나 이상이 있다면 false가 리턴된다.

부분집합에 관한 것도 살펴보자.
oddNumbers.isSubsetOf(evenNumbers)
// false

oddNumbers.isSubsetOf(Set([1, 3, 5, 7, 9, 11]))
// true

oddNumbers.isSupersetOf(Set([1, 3, 5, 7, 9, 11, 13]))
// false

oddNumbers.isSupersetOf(Set([1, 3, 5]))
// true
isSubsetOf는 자기 자신이 특정 Set의 부분집합(Subset)인지 파악하는 메소드이다. 반대로, isSupersetOf 메소드는 자기 자신이 특정 집합을 포함하고 있는지 파악이 가능하다. (진부분집합 이라는 표현을 써야겠지만 헷갈려서 -_-)

합집합과 교집합을 살펴보자.
oddNumbers.union(evenNumbers)
// {10, 2, 4, 5, 7, 6, 3, 1, 8}

oddNumbers.intersect(Set([1, 5, 11]))
// {5, 1}
union 메소드는 자기 자신과 특정 Set을 합친다. 즉 합집합을 만든다. 반대로 intersect 메소드는 자기 자신과 특정 Set 간에 겹치는 내용만 리턴한다. 즉 교집합을 만든다.

그 외에 다양한 기능이 있다. 예를 들어 특정 Set의 데이터를 빼 내는 경우
oddNumbers.subtract(Set([3, 5]))
// {7, 1}
이런 식으로 subtract 메소드를 활용 할 수 있다.

마무리

기존 파운데이션(Foundation)에 NSSet이나 NSMutableSet 같은 비슷한 자료구조가 이미 구현되어 있지만, 이를 Swift식으로 매핑하는 작업이 진행되고 있는 것으로 보인다. 좀 바뀔 가능성이 없는 것은 아니겠지만 일단 사용법은 간단해 보인다.

그나저나 이런 집합류 자료구조가 굉장히 많은 것 같다. NSSet 말고도 NSHashTable 이나 NSMapTable 같은 비스무리한 녀석들이 참 많다. 물론 성능과 용도가 요금씩 다르긴 하겠지만... :-)

[관련글] 스위프트(Swift) 가이드
[관련글] Swift 1.2 에서 바뀌는 것들
[관련글] Swift - 컬렉션 타입(Collection Types)

댓글 2개 :

케이크류 :

중간에

oddNumbers.insert(1)
oddNumbers.insert(3) // returns 'nil'
oddNumbers.insert(1)

에서

oddNumbers.insert(1)
oddNumbers.insert(3)
oddNumbers.insert(1) // returns 'nil'

가 아닌가 합니다.

Renn Seo :

@케이크류: 제가 실수를 했네요. 바로 고치겠습니다. 감사합니다. :-)