본문 바로가기
iOS

[Swift] 스위프트 프로그래밍 - Part1 - 3 - 1 : 데이터 타입 고급 (Tuple, Array, Dictionary, Set)

by 건복치 2020. 12. 21.
반응형
야곰님의 스위프트 프로그래밍 책으로 공부한 내용을 잊어버리지 않게 간단하게 정리한 글입니다.

관련 포스트

[Swift] 스위프트 프로그래밍 - Part1 - 1 : 스위프트? / 스위프트 장점 / 특징 / 명명 규칙 / 콘솔 로그 / 주석
[Swift] 스위프트 프로그래밍 - Part1 - 2 : 변수 / 상수 / 데이터 타입 기본 (Int, Bool, Float, Character, String, Any, AnyObject, nil)
[Swift] 스위프트 프로그래밍 - Part1 - 3 - 1 : 데이터 타입 고급 (Tuple, Array, Dictionary, Set)
[Swift] 스위프트 프로그래밍 - Part1 - 3 - 2 : 데이터 타입 고급(열거형 enum)

책 범위 : Part 1. 스위프트 기초 (4. 데이터 타입 고급)

1. 데이터 타입 안심

스위프트의 특징 중 안전성(safe)이 가장 뚜렷하게 나타나는 부분.

스위프트는 데이터 타입을 안심하고 사용할 수 있는 Type safe 언어 -> 그만큼 실수를 줄일 수 있다는 의미

1-1. 타입 확인

스위프트가 컴파일 시 타입을 확인하는 것 

 

var num: Int = "minha" // 타입 확인 -> 컴파일 오류

 

1-2. 타입 추론

변수나 상수를 선언할 때 특정 타입을 명시하지 않아도 컴파일러가 할당된 값을 기준으로 변수나 상수의 타입을 결정하는 것

var name = "minha" // 타입 추론에 의해 name은 String 타입으로 선언

name = 100 // String 변수에 정수할당 시 오류 발생

 

2. 타입 별칭

이미 존재하는 데이터 타입에 임의로 다른 이름(별칭)을 부여할 수 있는 것

기본 타입 이름과 이후에 추가한 별칭 모두 사용 가능

 

typealias MyInt = Int
typealias yourInt = Int

let age: MyInt = 26
var year: yourInt = 2020
var month: Int = 12

year = age //MyInt, yourInt 모두 Int로 같은 취급 

print("현재는 \(year)년 \(month)월, 나이는 \(age)살 입니다.") //현재는 26년 12월, 나이는 26살 입니다.

3. 튜플 (Tuple)

  • 타입의 이름이 따로 지정되어 있지 않은, 프로그래머 마음대로 만드는 타입
  • 지정된 데이터의 묶음
  • ex. C언어의 원시 구조체 형태와 비슷 
  • 인덱스를 통해 값 추출 및 할당이 가능
var person: (String, Int, Double) = ("minha", 100, 180.5)

// 인덱스를 통해 값 추출 가능
print("이름 : \(person.0), 나이 : \(person.1), 키 : \(person.2)")

// 인덱스를 통해 값 할당 가능
person.1 = 44
person.2 = 165.4
print("이름 : \(person.0), 나이 : \(person.1), 키 : \(person.2)")

출력

이름 : minha, 나이 : 100, 키 : 180.5
이름 : minha, 나이 : 44, 키 : 165.4

 

 

 

  • 튜플의 요소마다 이름을 붙여줄 수도 있음
// 튜플의 각 요소에 이름 부여
var person2: (name: String, age: Int, height: Double) = ("minha", 99, 190.5)

// 요소 이름을 통해 값 추출 가능
print("이름 : \(person2.name), 나이 : \(person2.age), 키 : \(person2.height)")

// 요소 이름을 통해 값 할당 가능
person2.age = 88
person2.2 = 110.12

// 요소 이름을 통해 값 추출 가능
print("이름 : \(person2.0), 나이 : \(person2.1), 키 : \(person2.height)")

출력

이름 : minha, 나이 : 99, 키 : 190.5
이름 : minha, 나이 : 88, 키 : 110.12

 

 

  • 타입 별칭을 사용해 매번 같고, 긴 튜플 타입을 모두 선언할 필요 없이 깔끔하고 안전하게 코드 작성 가능
// 튜플 별칭 지정
typealias PersonTuple = (name: String, age: Int, height: Double)

let minha: PersonTuple = ("minha", 100, 154.5)
let kwon: PersonTuple = ("kwon", 35, 175.3)

출력

이름 : minha, 나이 : 100, 키 : 154.5
이름 : kwon, 나이 : 35, 키 : 175.3

4. 컬렉션형

스위프트는 튜플 외에도 많은 수의 데이터를 묶어서 저장하고 관리할 수 있는 컬렉션 타입을 제공한다.

배열(Array), 딕셔너리(Dictionary), 세트(Set) 등이 있다.

5. 배열 (Array)

  • 같은 타입의 데이터를 일렬로 나열한 후 순서대로 저장하는 형태의 컬렉션 타입
  • 값 중복 가능
  • let 키워드를 사용해 상수로 선언하면 변경할 수 없는 배열, var 키워드를 사용해 변수로 선언해주면 변경 가능한 배열
  • Array 키워드와 타입 이름의 조합으로 사용
  • 대괄호로 값을 묶어 배열 타입 표현 가능
var names: Array<String> = ["minha", "kwon", "okju", "bokchi"]

//[String]은 Array<String>의 축약 표현
let names2: [String] = ["minha", "kwon", "okju", "bokchi"]

 

 

  • 빈 배열은 이니셜 라이저 또는 리터럴 문법을 통해 생성해 줄 수도 있음
var emptyArray: [Any] = [Any](); // Any 데이터를 요소로 갖는 빈 배열 생성
var emptyArray2: [Any] = Array<Any>(); // 위와 같은 코드
var emptyArray3: [Any] = [] // 배열의 타입을 정확히 명시했다면 []만으로도 빈 배열 생성 가능 

 

 

  • isEmpty 프로퍼티 :  빈 배열인지 확인
  • count 프로퍼티 : 배열에 몇 개의 요소가 존재하는지 확인
print(emptyArray.isEmpty) // true
print(names.isEmpty) // false
print(names.count) // 4

 

  • 각 요소에 인덱스를 통해 접근 가능

  • 인덱스는 0부터 시작

  • 잘못된 인덱스에 접근하면 익셉션 오류(Exception Error) 발생

  • C언어의 배열처럼 한 번 선언하면 크기가 고정되는 버퍼가 아니라, 필요에 따라 자동으로 버퍼의 크기를 조절해 요소의 삽입 및 삭제가 자유로움 

 

  • 맨 처음과 맨 마지막 요소는 first last 프로퍼티를 통해 가져올 수 있음

  • firstIndex(of:) 메서드 : 해당 요소의 인덱스를 알아낼 수 있음 (중복 시 제일 먼저 발견된 요소의 인덱스 반환)

  • append(_:) 메서드 : 맨 뒤에 요소를 추가할 때 사용

  • insert(_:at:) 메서드 : 중간에 요소를 삽입할 때 사용

  • remove(_:) 메서드 : 요소를 삭제할 때 사용 (삭제된 후 반환됨)

  • array[1 ... 3] : 범위 연산자를 사용해 array 배열의 일부만 가져온 것 / array[1 ... 3] = ["A", "B", "C"]와 같이 요소 변경도 가능
print(names[2])
names[2] = "juok"
print(names[2])
// print(names[4]) // 인덱스의 범위를 벗어나 익셉션 오류 발생
// names[4] = "elsa" // 오류

names.append("Elsa")
names.append(contentsOf: ["Anna", "Olaf"])
names.insert("Chris", at: 3)
names.insert(contentsOf: ["Sven", "Frozen"], at: 5)
print(names[0 ... names.count-1])

print(names.firstIndex(of: "minha"))
print(names.firstIndex(of: "mickey"))
print(names.first)
print(names.last)

let firstItem: String = names.removeFirst()
let lastItem: String = names.removeLast()
let indexZeroItem: String = names.remove(at: 0)

print(firstItem)
print(lastItem)
print(indexZeroItem)
print(names[1 ... 3])

출력

okju
juok
["minha", "kwon", "juok", "Chris", "bokchi", "Sven", "Frozen", "Elsa", "Anna", "Olaf"]
Optional(0)
nil
Optional("minha")
Optional("Olaf")
minha
Olaf
kwon
["Chris", "bokchi", "Sven"]

6. 딕셔너리 (Dictionary)

  • 요소들이 순서 없이 키와 값의 쌍으로 구성되는 컬렉션 타입

  • 키는 하나이거나 여러개일 수 있음

  • 키는 같은 이름을 중복해서 사용할 수 없음 (값을 대변하는 유일한 식별자)

  • 각 값에 키로 접근 가능

  • 키는 유일 / 값은 유일하지 않음

  • 배열과 달리 딕셔너리 내부에 없는 키로 접근해도 오류 발생 안 함 -> nil 반환

// typealias를 통해 단순하게 표현 가능
typealias StringIntDictionary = [String: Int]

// 키는 String, 값은 Int 타입인 빈 딕셔너리 생성
var numberForName: Dictionary<String, Int> = Dictionary<String, Int>()

// 위 선언과 같은 표현 / [String: Int] 는 Dictionary<String, Int>의 축약 표현
var numberForName2: [String: Int] = [String: Int]()

// 위 코드와 같은 동작
var numberForName3 : StringIntDictionary = StringIntDictionary()

 

  • Dictionary 키워드와 키의 타입, 값의 타입 이름의 조합으로 사용 var dic: Dictionary<String, Int> = Dictionary<String, Int>()
  • 대괄호로 키와 값의 타입 이름의 쌍을 묶어 딕셔너리 타입임을 표현 var dic: [String, Int] = [String: Int]()
// 딕셔너리의 키와 값 타입을 정확히 명시해줬다면 [:]만으로도 빈 딕셔너리 생성 가능
var numberForName4: [String: Int] = [:]

// 초기값을 주어 생성
var numberForName5: [String: Int] = ["minha": 100, "elsa": 200, "anna": 300]

 

  • let 키워드를 사용해 상수로 선언하면 변경 불가능한 딕셔너리,  var 키워드를 사용해 변수로 선언하면 변경 가능한 딕셔너리 
  • 빈 딕셔너리는 이니셜라이저 또는 리터럴 문법을 통해 생성 가능 

 

  • isEmpty 프로퍼티 : 빈 딕셔너리인지 확인
  • count : 딕셔너리 요소의 개수 확인
  • RemoveValue(forKey:) 메서드 : 특정 키에 해당하는 값 제거(제거 후 반환)
print(numberForName5["minha"])
print(numberForName5["olaf"])

numberForName5["minha"] = 400
print(numberForName5["minha"])

numberForName5["olaf"] = 999 // olaf라는 키로 999라는 값을 추가
print(numberForName5["olaf"])

print(numberForName5.removeValue(forKey: "anna"))
print(numberForName5.removeValue(forKey: "anna")) // 위에서 이미 anna에 해당하는 값이 삭제되었으므로 nil 반환
print(numberForName5["anna", default: 0]) // 키에 해당하는 값이 없으면 기본으로 0 반환

출력

Optional(100)
nil
Optional(400)
Optional(999)
Optional(300)
nil
0

7. 세트 (Set)

  • 같은 타입의 데이터를 순서 없이 하나의 묶음으로 저장하는 형태의 컬렉션 타입

  • Set내의 같은 모두 유일한 값, 중복된 값 존재 X

  • 순서가 중요하지 않거나 각 요소가 유일한 값이어야 하는 경우 사용

  • Set의 요소는 해시 가능한 값이 여야 한다. (Hashable 프로토콜을 다른다는 것의 의미. 기본 데이터 타입은 모두 해시 가능한 값)

var frozen: Set<String> = Set<String>() // 빈 Set 생성
var frozen2: Set<String> = [] // 빈 Set 생성

frozen = ["elas", "anna", "olaf", "chris", "sven"] // 배열과 마찬가지로 대괄호 사용

var numbers = [100, 200, 300] // 따라서 타입 추론을 사용할 경우, 컴파일러는 Set이 아닌 Array로 타입을 지정함

 

  • Set 키워드와 타입 이름의 조합으로 사용 var set: Set<String> = Set<String>()

  • 대괄호로 값들을 묶어 Set 타입임을 표현 var set: Set<String> = []

  • 배열과 달리 축약형이 없다. (Array<Int> 를 [Int]로 축약하는 것)

  • let 키워드를 사용해 상수로 선언하면 변경 불가능한 Set,  var 키워드를 사용해 변수로 선언하면 변경 가능한 Set

  • 빈 Set는 이니셜 라이저 또는 리터럴 문법을 통해 생성할 수 있음

 

  • isEmpty 프로퍼티 :  빈 세트인지 혹인
  • count 프로퍼티 : 세트의 요소 개수 확인
  • insert(_:) 메서드 : 요소 추가
  • remove(_:) 메서드  : 요소 삭제 (삭제된 후 반환)
print(frozen.isEmpty)
print(frozen.count)

frozen.insert("iduna")

print(frozen.remove("chris"))
print(frozen.remove("mickey"))

출력

false
5
Optional("chris")
nil

 

 

  • 집합 관계를 표현하고자 할 때 유용하게 사용됨 (두 Set의 교집합, 합집합 연산 등)
  • sorted() 메서드 : 정렬된 배열 반환 
  • 포함 관계를 연산할 수 있는 메서드로 구현되어 있다.
let frozenCharacters: Set<String> = ["elas", "anna", "olaf", "chirs", "sven"]
let disneyCharacters: Set<String> = ["elas", "anna", "olaf", "woody", "rapunzel", "buzz"]

// 교집합
let intersectSet: Set<String> = frozenCharacters.intersection(disneyCharacters)
print(intersectSet)

// 여집합의 합 (배타적 논리합)
let symmetricDiffSet: Set<String> = frozenCharacters.symmetricDifference(disneyCharacters)
print(symmetricDiffSet)

// 합집합
let unionSet: Set<String> = frozenCharacters.union(disneyCharacters)
print(unionSet)

// 차집합
let subtractSet: Set<String> = frozenCharacters.subtracting(disneyCharacters)
print(subtractSet)

// 합집합 정렬
print(unionSet.sorted())

let 새: Set<String> = ["까마귀", "닭", "학"]
let 포유류: Set<String> = ["사자", "곰", "호랑이"]
let 동물: Set<String> = 새.union(포유류)

print(새.isDisjoint(with: 포유류)) // 새로 배타적인지 - true
print(새.isSubset(of: 포유류)) // 새가 동물의 부분집한인지? - true
print(동물.isSuperset(of: 새))// 동물은 포유류의 전체집합인지? - true
print(동물.isSuperset(of: 포유류))// 동물의 새의 전체집합인지? - true

출력

["elas", "olaf", "anna"]
["buzz", "sven", "rapunzel", "chirs", "woody"]
["chirs", "sven", "olaf", "anna", "rapunzel", "elas", "buzz", "woody"]
["sven", "chirs"]
["anna", "buzz", "chirs", "elas", "olaf", "rapunzel", "sven", "woody"]
true
false
true
true

(번외) 컬렉션에서 임의의 요소 추출과 뒤섞기

스위프트 4.2 버전부터 컬렉션에서 임의의 요소를 추출하고 뒤섞응 메서드가 추가되었다.

 

  • randomElement() 메서드 : 컬렉션에서 임의의 요소 추출

  • shuffle() 메서드 : 컬렉션의 요소를 임의로 뒤섞음

  • shuffled() 메서드 : 자신의 요소는 그대로 둔 채 새로운 컬렉션에 임의의 순서로 섞어서 반환

var array: [Int] = [0, 1, 2, 3, 4]
var set: Set<Int> = [0, 1, 2, 3, 4]
var dictionary: [String: Int] = ["a": 1, "b": 2, "c": 3]
var string: String = "string"

print(array.randomElement()) // 임의의 요소
print(array.shuffled()) // 뒤죽박죽된 배열 - 기존 array 내부의 요소는 그대로 있음
print(array)
array.shuffle() // array 자체를 뒤죽박죽으로 뒤섞음
print(array)

print(set.shuffled()) // Set을 뒤섞으면 배열로 반환해줌
// set.shuffle() // Set은 순서가 없기 때문에 스스로 뒤섞을 수 없음
print(dictionary.shuffled()) //  Dictionary를 섞으면 (키, 값)이 쌍을 이룬 튜플의 배열로 반환해줌
print(string.shuffled()) // String도 컬렉션임 

 

Optional(0)
[3, 2, 0, 1, 4]
[0, 1, 2, 3, 4]
[3, 1, 4, 0, 2]
[4, 1, 0, 2, 3]
[(key: "b", value: 2), (key: "a", value: 1), (key: "c", value: 3)]
["g", "r", "n", "t", "i", "s"]

전체 코드

전체 예제 코드를 확인하고 싶다면 '더보기'를 눌러주세요.

더보기
print("4.2 타입 별칭 -----------------------\n")
typealias MyInt = Int
typealias yourInt = Int

let age: MyInt = 26
var year: yourInt = 2020
var month: Int = 12

year = age //MyInt, yourInt 모두 Int로 같은 취급

print("현재는 \(year)년 \(month)월, 나이는 \(age)살 입니다.")




print("\n4.3 튜플 -----------------------\n")
var person: (String, Int, Double) = ("minha", 100, 180.5)

// 인덱스를 통해 값 추출 가능
print("이름 : \(person.0), 나이 : \(person.1), 키 : \(person.2)")

// 인덱스를 통해 값 할당 가능
person.1 = 44
person.2 = 165.4
print("이름 : \(person.0), 나이 : \(person.1), 키 : \(person.2)")


// 튜플의 각 요소에 이름 부여
var person2: (name: String, age: Int, height: Double) = ("minha", 99, 190.5)

// 요소 이름을 통해 값 추출 가능
print("이름 : \(person2.name), 나이 : \(person2.age), 키 : \(person2.height)")

// 요소 이름을 통해 값 할당 가능
person2.age = 88
person2.2 = 110.12

// 요소 이름을 통해 값 추출 가능
print("이름 : \(person2.0), 나이 : \(person2.1), 키 : \(person2.height)")

// 튜플 별칭 지정
typealias PersonTuple = (name: String, age: Int, height: Double)

let minha: PersonTuple = ("minha", 100, 154.5)
let kwon: PersonTuple = ("kwon", 35, 175.3)

print("이름 : \(minha.0), 나이 : \(minha.1), 키 : \(minha.2)")
print("이름 : \(kwon.name), 나이 : \(kwon.age), 키 : \(kwon.height)")




print("\n4.5 배열 -----------------------\n")
var names: Array<String> = ["minha", "kwon", "okju", "bokchi"]

//[String]은 Array<String>의 축약 표현
let names2: [String] = ["minha", "kwon", "okju", "bokchi"]

var emptyArray: [Any] = [Any](); // Any 데이터를 요소로 갖는 빈 배열 생성
var emptyArray2: [Any] = Array<Any>(); // 위와 같은 코드
var emptyArray3: [Any] = [] // 배열의 타입을 정확히 명시했다면 []만으로도 빈 배열 생성 가능

print(emptyArray.isEmpty)
print(names.isEmpty)
print(names.count)


print("\n4.5 배열의 사용 -----------------------\n")
print(names[2])
names[2] = "juok"
print(names[2])
// print(names[4]) // 인덱스의 범위를 벗어나 익셉션 오류 발생
// names[4] = "elsa" // 오류

names.append("Elsa")
names.append(contentsOf: ["Anna", "Olaf"])
names.insert("Chris", at: 3)
names.insert(contentsOf: ["Sven", "Frozen"], at: 5)
print(names[0 ... names.count-1])

print(names.firstIndex(of: "minha"))
print(names.firstIndex(of: "mickey"))
print(names.first)
print(names.last)

let firstItem: String = names.removeFirst()
let lastItem: String = names.removeLast()
let indexZeroItem: String = names.remove(at: 0)

print(firstItem)
print(lastItem)
print(indexZeroItem)
print(names[1 ... 3])




print("\n4.6 딕셔너리 -----------------------\n")
// typealias를 통해 단순하게 표현 가능
typealias StringIntDictionary = [String: Int]

// 키는 String, 값은 Int 타입인 빈 딕셔너리 생성
var numberForName: Dictionary<String, Int> = Dictionary<String, Int>()

// 위 선언과 같은 표현 / [String: Int] 는 Dictionary<String, Int>의 축약 표현
var numberForName2: [String: Int] = [String: Int]()

// 위 코드와 같은 동작
var numberForName3 : StringIntDictionary = StringIntDictionary()

// 딕셔너리의 키와 값 타입을 정확히 명시해줬다면 [:]만으로도 빈 딕셔너리 생성 가능
var numberForName4: [String: Int] = [:]

// 초기값을 주어 생성
var numberForName5: [String: Int] = ["minha": 100, "elsa": 200, "anna": 300]

print(numberForName.isEmpty)
print(numberForName5.count)


print("\n4.6 딕셔너리의 사용 -----------------------\n")
print(numberForName5["minha"])
print(numberForName5["olaf"])

numberForName5["minha"] = 400
print(numberForName5["minha"])

numberForName5["olaf"] = 999 // olaf라는 키로 999라는 값을 추가
print(numberForName5["olaf"])

print(numberForName5.removeValue(forKey: "anna"))
print(numberForName5.removeValue(forKey: "anna")) // 위에서 이미 anna에 해당하는 값이 삭제되었으므로 nil 반환
print(numberForName5["anna", default: 0]) // 키에 해당하는 값이 없으면 기본으로 0 반환




print("\n4.7 세트 -----------------------\n")
var frozen: Set<String> = Set<String>() // 빈 Set 생성
var frozen2: Set<String> = [] // 빈 Set 생성

frozen = ["elas", "anna", "olaf", "chris", "sven"] // 배열과 마찬가지로 대괄호 사용

var numbers = [100, 200, 300] // 따라서 타입 추론을 사용할 경우, 컴파일러는 Set이 아닌 Array로 타입을 지정함
print(type(of: numbers)) // Array<Int>


print("\n4.7 세트의 사용 -----------------------\n")
print(frozen.isEmpty)
print(frozen.count)

frozen.insert("iduna")
print(frozen.remove("chris"))
print(frozen.remove("mickey"))

let frozenCharacters: Set<String> = ["elas", "anna", "olaf", "chirs", "sven"]
let disneyCharacters: Set<String> = ["elas", "anna", "olaf", "woody", "rapunzel", "buzz"]

// 교집합
let intersectSet: Set<String> = frozenCharacters.intersection(disneyCharacters)
print(intersectSet)

// 여집합의 합 (배타적 논리합)
let symmetricDiffSet: Set<String> = frozenCharacters.symmetricDifference(disneyCharacters)
print(symmetricDiffSet)

// 합집합
let unionSet: Set<String> = frozenCharacters.union(disneyCharacters)
print(unionSet)

// 차집합
let subtractSet: Set<String> = frozenCharacters.subtracting(disneyCharacters)
print(subtractSet)

// 합집합 정렬
print(unionSet.sorted())

let 새: Set<String> = ["까마귀", "닭", "학"]
let 포유류: Set<String> = ["사자", "곰", "호랑이"]
let 동물: Set<String> = 새.union(포유류)

print(새.isDisjoint(with: 포유류)) // 새로 배타적인지 - true
print(새.isSubset(of: 포유류)) // 새가 동물의 부분집한인지? - true
print(동물.isSuperset(of: 새))// 동물은 포유류의 전체집합인지? - true
print(동물.isSuperset(of: 포유류))// 동물의 새의 전체집합인지? - true




print("\n번외. 컬렉션에서 임의의 요소 추출과 뒤섞기 -----------------------\n")
var array: [Int] = [0, 1, 2, 3, 4]
var set: Set<Int> = [0, 1, 2, 3, 4]
var dictionary: [String: Int] = ["a": 1, "b": 2, "c": 3]
var string: String = "string"

print(array.randomElement()) // 임의의 요소
print(array.shuffled()) // 뒤죽박죽된 배열 - 기존 array 내부의 요소는 그대로 있음
print(array)
array.shuffle() // array 자체를 뒤죽박죽으로 뒤섞음
print(array)

print(set.shuffled()) // Set을 뒤섞으면 배열로 반환해줌
// set.shuffle() // Set은 순서가 없기 때문에 스스로 뒤섞을 수 없음
print(dictionary.shuffled()) //  Dictionary를 섞으면 (키, 값)이 쌍을 이룬 튜플의 배열로 반환해줌
print(string.shuffled()) // String도 컬렉션임

참고

아래를 참고해 정리한 내용입니다.

 

스위프트 프로그래밍 3판(야곰) - 한빛미디어
반응형

댓글