32YB SOPT/swift 문법 스터디

[SOPT] 야우쓰_3주차 (열거형 튜플, enum, Associated Type)

신_이나 2023. 5. 10. 04:00

 

 

야우쓰란? "야 우리도 스위프트 할 수 있어"

in SOPT iOS

 

 

 

 

 

 

 

 

3주차의 키워드는 열거형 튜플이다.

 

 

 

- 튜플

 튜플(tuple)이란 셀 수 있는 수량의 순서 있는 열거다. swift 에선 여러 개의 데이터를 모은 집합으로 추가와 삭제가 가능하다. 가장 큰 특징은 다른 타입을 사용해도 상관없다! 그렇다면 바로 예시를 살펴보자!

 

//1. 
var tuple : (String, Int, Bool) = ("My age is", 20, false)
var ttuple = ("jiwon is", "beautiful", true)

//Prints
//("My age is", 20, false)
//("jiwon is", " beautiful", true)


//2.
var tuple : (String, Int, Bool) = ("My age is", 20, false)
var (text, age, fact) = tuple
print(text, age, fact)

//Prints My age is 20 false


//3.
var tuple : (String, Int, Bool) = ("My age is", 20, false)
print(tuple.0, tuple.1, tuple.2)

//Prints My age is 20 false

 

++ 배열과 다른 점

튜플은 변경할 수 없는 고정된 집합이지만 배열은 항목을 무기한 추가할 수 있다. 튜플은 여러 가지 타입을 저장할 수 있지만 배열은 한 가지 타입만 저장할 수 있다.

 

++ 타입이 너무 길때, 사용할 수 있는 꿀팁

typealias 를 사용할 수 있다.

typealias MyData = (Int, Double, String)
var tuple : MyData = (1, 1.0, "jiwon")

print(tuple)

//Prints (1, 1.0, "jiwon")

 

++ for 문에서 사용가능

let fruits = ["A": 10, "B": 20, "C": 30]

for (key, value) in fruits {
    print(key, value)
}

fruits 안에 있는 값들을 튜플안에 담아 for 문으로 출력할 수 있다. 그렇다면 튜플 자체를 for 문으로 출력할 수는 없는 것일까? 예를 들어 아래와 같이 ,,, 근데 에러가 난다.

var tuple : (String, Int, Bool) = ("jiwon", 20, false)

for i 0...3 {
    print(tuple.i)
}

 

 

++ Question

1-1. 아래 코드는 에러가 난다. 어떻게 해결해야 할까?

1-2. 만약 내가 introduction값이 필요 없다면 어떤 식으로 표현해야 할까?

let person = (name: "희재", age: 24, introduction: "저는 낭만보이입니다")

let (name, age) = person 
let (name, age, introduction, gender) = person

 

 

1-1. 

우선 에러 내용을 살펴보자. 3번째 줄과 4번째 줄에서 같은 에러 메세지가 발생하였다. "'(name: String, age: Int, introduction: String)' is not convertible to '(String, Int)', tuples have a different number of elements" 라는 에러로 튜플의 요소가 다르기 때문에 반환할 수 없다고 나왔다. 따라서 출력하고자 하는 바만 출력해준다면 해결해 줄 수 있다.

let person = (name: "희재", age: 24, introduction: "저는 낭만보이입니다", gender : "Man")

//let (name, age) = person
print(person.0, person.1)

//let (name, age, introduction, gender) = person
print(person.name, person.age, person.introduction, person.gender)

//Prints 
//희재 24
//희재 24 저는 낭만보이입니다 Man

 

1-2. 

사용하지 않는 튜플 요소는 언더바(_)로 대체할 수 있다. 컴파일러는 언더바로 표현된 부분을 할당하지 않고 지나간다.

let person = (name: "희재", age: 24, introduction: "저는 낭만보이입니다")
let (name, age, _) = person

print(person)
//Prints (name: "희재", age: 24, introduction: "저는 낭만보이입니다")

 whildcard pattern 을 통해 언더바를 통해 무시할 수 있다는 것은 알았다. 하지만 person 을 출력해보니 introduction은 출력되지 않길 바랬던 것과 달리 처음과 동일하게 출력되었다. 그렇다면 값 자체를 바꾸는 것이 아닌 단순히 어떤 상황이 닥쳤을 때(조건문 등) 무시한다는 의미로 받아들여도 괜찮을까?

 

 

1-3. 

let tuple = (171, 80)

switch tuple{
    //초과 범위 연산자를 사용자 설정하려고 했는데 실패했어요 ,,,
case (172..<174, 81..<85):
    print("류뚱의 비율과 비슷하시군요?")
case (174, 85):
    print("류뚱의 몸매시군요?")
case (_, 85):
    print("류뚱의 몸무게랑 같군요?")
case (174, _):
    print("류뚱의 키와 같군요?")
default:
    print("아무것도 아니시군요")
}

 

 

 

 

- Enum

네? 무슨 설명 안나와요,,,????

enum 은 열거형으로 자동으로 열거해주는(?) 성질을 가지고 있다. 이것도 바로 예시로 보는 것이 빠를 것 같다.

enum list: Int {
    case a //0
    case b //1
    case c //2
    case d //3
    case e //4
}

enum list: String {
    case a //a
    case b //b
    case c //c
    case d //d
    case e //e
}

단순히 print(list.a) 를 작성한다면 case 에 선언된 element 를 불러온다. Int 로 선언된 0이란 값을 불러오고 싶다면 print(list.a.rawValue) 를 작성해야 한다. Int 는 선언된 값에서 다음 요소에 자동으로 +1 씩 선언된다. String 으로 선언된 값은 어떤 값도 지정하지 않았을 때 자동으로 case 이름과 같은 string 을 선언한다. 하지만 Character Type 은 하나하나 설정해 주어야 한다.

 

 

 

 

 

++ Question

 

2-1. design, ios, server는 어떤 값일지 유추해보세요!

enum Sopt: Int {
		case plan = 0
		case design 
		case aos = 10
		case ios 
		case web = 30
		case server 
}
enum Sopt: Int {
		case plan = 0
		case design //1
		case aos = 10
		case ios //11
		case web = 30
		case server //31
}

 

 

2-2. Associated Value 를 이용하여 아래 내용을 코드로 구현해보세요!

++ 이걸 하기 위해서 여러가지 개념을 추가로 공부해야 함

++ Associated Value 이란?

 

Associated Value 이란 단어 그대로는 "연관값"을 의미한다. 따라서 연관값의 대표적인 예시인 tuple 사용하여 위의 문제를 해결할 수 있다.

 

import Foundation

enum Part {
    case plan
    case design
    case android
    case iOS(havemacbook : Bool)
    case web
    case server
}

func volunteer(part: Part, name: String){
    
    switch part {
    case .plan:
        print("당신은 멋진 기획자가 될거에요!")
    case .iOS(let havemacbook):
        if(havemacbook) { print("맥북으로 멋지게 아요코딩하자궁!") }
        else { print("잠깐만! \(part)는 맥북이 필수라는 점~!") }
    case .web:
        print("\(part)의 \(name) 당신은 최고의 웹 개발자입니다!")
    default:
        break
    }
    
}

//Prints
volunteer(part: .plan, name: "jiwon")
volunteer(part: .design, name: "jiwon")
volunteer(part: .android, name: "jiwon")
volunteer(part: .iOS(havemacbook: true), name: "jiwon")
volunteer(part: .iOS(havemacbook: false), name: "jiwon")
volunteer(part: .web, name: "jiwon")
volunteer(part: .server, name: "jiwon")

/*
당신은 멋진 기획자가 될거에요!
맥북으로 멋지게 아요코딩하자궁!
잠깐만! iOS(havemacbook: false)는 맥북이 필수라는 점~!
web의 jiwon 당신은 최고의 웹 개발자입니다!
*/

 

 

 

 

Associated Value 와 비슷한 단어로 Associated Type 라는 것도 있다!

++ generic 이란?

 타입에 의존하지 않는 타입으로 범용 코드를 작성할 때 사용 가능하다. 

//before int 와 double 이기 때문에 swap 할 때 어려움!
func swapTwoInts(_ a: inout Int, _ b: inout Double) {
    let temporaryA = a
    a = b
    b = temporaryA
}

//after
func swapTwoInts<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}

 

++ Associated Type 이란?

  protocol 에서 사용될 임의의 타입이름을 주는 것이다. generic 을 protocol 에 접목시킨 것이다. Protocol 은 지난 세미나 복습에서 정리해두었다. 사용법은 protocol 내부에 associated type 을 선언하고 범용타입의 이름을 선언하면 된다. 타입에 제한을 주고 싶다면 equtable 을 사용하여 가능하다. equtable 은 추후 더 공부하자!

 

 

2-3. 어떤 상황에서 Enum을 사용하면 좋을까요?

 enum 과 protocol 을 많이 비교한다. 보통 enum은 사용자가 나타낼 수 있는 모든 데이터를 정의하는 경우에 사용된다. 또한 특정 값에만 조건이 필요한 경우에도 불필요하게 switch 문을 사용해야 하기 때문에 모든 값에 조건이 필요한 경우 더 유용할 것이다. 따라서 가독성을 높이고 더 직관적인 코드를 구현할 수 있다는 점이 장점이다. 반면 protocol 은 enum의 단점을 보완할 수 있다. 예를 들어, 추가로 다른 데이터를 정의할 때나 하나의 조건에만 값이 필요하다 하더라도 모든 값을 고려해주지 않고 추가로 조건문을 붙여주면 되기 때문에 더 효율적으로 사용할 수 있다.