본문 바로가기

SWIFT 공식문서 문법 알아보기/04. Collection Types

Swift 공식문서 해설 Collection Types - Sets, Performing Set Operations (4-2)

Sets

Set은 순서없이 같은 타입의 값을 저장합니다. 아이템의 순서가 중요하지 않거나 아이템이 오직 한번만 나타날 때 배열을 대신해서 set을 활용합니다. 

NOTE

스위프트의 set타입은 Foundation’s의 NSSet class와 연결되어있습니다. Foundation 과 Cocoa와 함께set을 사용하는 방법에 대해 알고 싶다면 Bridging Between Set and NSSet를 확인하세요.

Hash Values for Set Types

set에 저장되어지기 위해서는 타입은 반드시 hashable이어야 합니다. 즉 타입은 반드시 스스로 hash값을 계산할 수 있는 방법을 제공 해야합니다. hash값은 정수형값으로 만약 a == b 라면 a.hashValue == b.hashValue와 같이 동등하게 비교되는 모든 객체로 같아야합니다.

모든 스위프트의 기본 타입은 (String, Int, Double, Bool) 기본적으로 hashable이며 set값 타입이나 dictionary key 타입에사용될 수 있습니다. associated values가 없는 Enumeration case값도 기본적으로 hashable 합니다. 

NOTE

스위프트의 표준 라이브러리에서 Hashable protocol에 맞게 set 값 타입이나 dictionary key 타입을 사용할 수 있습니다. Hashable protocol을 따르는 타입들은 hashValue라고 불리는 gettable Int 속성을 제공해야 합니다. hashValue 속성 타입에 의해 반환되어지는 값은 같은 프로그램 혹은 다른 프로그램에서의 다른 실행이 꼭 같을 필요는 없습니다. Hashable protocol은 Equatable을 따르기 때문에 이를 따르는 타입들은 equals operator (==)의 실행을 제공해야합니다. Equatable protocol은 ==의 실행이 동등한 관계가 되도록 요구합니다. ==의 실행은 a, b, c 모든 값을 3가지 조건을 충족해야합니다. 

 

  • a == a (Reflexivity)
  • a == b implies b == a (Symmetry)
  • a == b && b == c implies a == c (Transitivity)

 

프로토콜에 대한 자세한 정보는 protocols을 참조하세요.

Set Type Syntax

스위프트 set 타입은 Set<Element>로 쓰여지고 Element는set을 저장하기 위해 허락되는 타입을 말합니다. 배열과 다르게 set은 축약형태가 없습니다.

Creating and Initializing an Empty Set

초기화 구문을 사용하여 특정한 타입의 비어있는 set을 만들수 있습니다. 

 

var letters = Set<Character>()
print("letters is of type Set<Character> with \(letters.count) items.")
// Prints "letters is of type Set<Character> with 0 items."

 

NOTE

letters 변수는 타입초기화로 부터 Set<Character>로 추정됩니다.

 

만약 문맥에서 함수 혹은 이미 타입지정된 변수나 상수와 같이 이미 타입에 대한 정보가 제공 된다면 empty array literal를 사용해서 비어있는 set을 만들 수 있습니다. 

 

letters.insert("a")
// letters now contains 1 value of type Character
letters = []
// letters is now an empty set, but is still of type Set<Character>

Creating a Set with an Array Literal

array literal을 사용하여 set을 초기화 할수 있고 한개 혹은 그 이상의 값을 set으로 축약하여 쓸 수 있습니다. 

아래 예는 favoriteGenre라고 불리는 문자열 값을 set으로 만들 었습니다. 

 

var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
// favoriteGenres has been initialized with three initial items

 

favoriteGenres 변수는 Set<String>을 사용함으로써 set은 문자열 값으로 선언되었습니다. 이 set은 문자열 타입의 값으로 명시되어졌기 때문에 문자열 값만 저장할수 있도록 허락되어집니다. favoriteGenres set은 array literal로 3개의 문자열("Rock", "Classical", and "Hip hop")을 초기화했습니다.

NOTE

아래의 예처럼 아이템을 추가하거나 제거할 수 있기 때문에 favoriteGenres set은 상수가 아닌 변수로 선언되었습니다. 

 

set 타입은 array literal 혼자로는 추론되어질 수 없기 때문에 set타입은 반드시 명확하게 선언해야합니다. 그러나 스위프트의 타입추론 때문에 만약 array literal을 초기화 한다면 포함된 값의 타입은 하나이기 때문에 set의 elements의 타입을쓸 필요가 없습니다. favoriteGenres의 초기화는 축약형으로 쓰여질 수 있습니다. 

 

var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]

 

array literal의 모든 값은 모두 같은 타입이기 때문에 favoriteGenres의 정확한 타입의 Set<String>을 추론할 수 있습니다.

Accessing and Modifying a Set

methods and properties을 통하여 set을 접근하고 변경할 수 있습니다.

set안에 있는 아이템의 갯수를 찾기 위해 오직 읽을 수 밖에 없는 count 속성으로 확인할 수 있습니다. 

 

print("I have \(favoriteGenres.count) favorite music genres.")
// Prints "I have 3 favorite music genres."

 

Boolean속성을 가지고 있는 isEmpty속성을 활용해서 count속성이 0인지 아닌지 빠르게 확인 할 수 있습니다. 

 

if favoriteGenres.isEmpty {
print("As far as music goes, I'm not picky.")
} else {
print("I have particular music preferences.")
}
// Prints "I have particular music preferences."

 

insert(_:) method를 사용하여 새로운 아이템을 추가할 수 있습니다. 

 

favoriteGenres.insert("Jazz")
// favoriteGenres now contains 4 items

 

remove(_:) method를 활용해서 아이템을 지울수 있고 만약 제거하고 싶은 아이템이 set에 포함되어있다면 값을 반환하고 제거하며 만약 아이템이 set에 포함되지 않았다면 nil 값을 반환합니다. set 안의 모든 아이템을 제거하고 싶다면 removeAll() method를 사용하면 됩니다. 

 

if let removedGenre = favoriteGenres.remove("Rock") {
print("\(removedGenre)? I'm over it.")
} else {
print("I never much cared for that.")
}
// Prints "Rock? I'm over it."

 

특정한 아이템이 포함되어 있는지 아닌지를 확인하기 위해 contains(_:) method를 사용하면 됩니다. 

 

if favoriteGenres.contains("Funk") {
print("I get up on the good foot.")
} else {
print("It's too funky in here.")
}
// Prints "It's too funky in here."

Iterating Over a Set

for-in loop를 사용해서 값을 반복하게 할 수 있습니다. 

 

for genre in favoriteGenres {
print("\(genre)")
}
// Classical
// Jazz
// Hip hop

 

for-in loop에 대해 자세이 알고 싶다면 for-in loop를 참조하세요.

 

Swift 공식문서 해설 Control Flow - For-In Loops, While Loops (5-1)

스위프트는 다양한 control flow 문장을 제공합니다. while loop는 어떤 일을 여러 번 시키는 데 사용하고 if, guard, switch 구문은 특정 상황에 따라 다른 코드를 실행시키며 break, continue와 같은 문장은 코..

iosbeginner.tistory.com

 

스위프트의 set타입은 순서를 정하지 않습니다. set을 특정한 순서로 값을 반복하기 원한다면 sorted() method를 사용하고 < 연산자를 사용해서 set의 elements를 정렬된 배열로 만듭니다. 

 

for genre in favoriteGenres.sorted() {
print("\(genre)")
}
// Classical
// Hip hop
// Jazz

 

Performing Set Operations

2개의 set을 결합하거나 2개의 set이 공통점있거나 혹은 2개의 set의 같은 값을 모두, 일부 전혀 포함하는지와 같이 기본적인 작업을 효율적으로 수행이 가능합니다. 

Fundamental Set Operations

아래의 그림은 2개의 set a,b를 set operations의 다양한 결과값을 음영에 의해 나타낸 것입니다. 

 

 

 

 

 

 

  • intersection(_:) method를 사용해서 2set의 공통의 값을 찾아 새로운 set를 만듭니다.
  • symmetricDifference(_:) method를 사용해서 2set의 값이 모두 아닌 새로운 set를 만듭니다. 
  • union(_:) method를 사용해서 2개의 set을 모두 포함하는 새로운 set을 만듭니다.
  • subtracting(_:) method을 사용해서 특정 set에 없는 값을 가진 새로운 set을 만듭니다.
let oddDigits: Set = [1, 3, 5, 7, 9]
let evenDigits: Set = [0, 2, 4, 6, 8]
let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]

oddDigits.union(evenDigits).sorted()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
oddDigits.intersection(evenDigits).sorted()
// []
oddDigits.subtracting(singleDigitPrimeNumbers).sorted()
// [1, 9]
oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted()
// [1, 2, 9]

Set Membership and Equality

아래의 그림은 a, b, c 3가지 set 간에 공유되는 요소를 겹치는 영역으로 묘사하고 있습니다. set a는 set b 의 모든 요소를 포함하고 있기 때문에 set a는 set b의 상위 집합입니다. 반대로 set b의 모든 요소는 set a에 포함되어지기 때문에 set b는 set a의 하위집합입니다.

 

 

  • is equal(==) 연산자를 활용해서 2개의 set이 모두 같은 값은 포함하는지 아닌지를 확인할 수 있습니다.
  • isSubset(of:) method를 활용해서 특정한 set의 모든 값이 포함되는지 아닌지를 확인할 수 있습니다.
  • isSuperset(of:) method를 활용해서 특정한 set 안에 set의 모든 값이 포함되는지 아닌지를 확인할 수 있습니다.
  • isStrictSubset(of:) or isStrictSuperset(of:) methods를 활용해서 특정한 set과 같지는 않지만 하위집합 혹은 상위집합 인지 아닌지를 확인할 수 있습니다. 
  • Disjoint(with:) method를 활용해서 2개의 set이 공통의 값이 있는지 없는지를 확인할 수 있습니다.
let houseAnimals: Set = ["🐶", "🐱"]
let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"]
let cityAnimals: Set = ["🐦", "🐭"]

houseAnimals.isSubset(of: farmAnimals)
// true
farmAnimals.isSuperset(of: houseAnimals)
// true
farmAnimals.isDisjoint(with: cityAnimals)
// true