본문 바로가기
iOS/SWIFT

[Swift] Chapter 06. 흐름 제어

by 원만사 2021. 10. 8.
반응형

 

6.1 조건문


6.1.1 if 구문

스위프트의 if 구문은 조건의 값이 꼭 Bool 타입이어야 한다.

 

/*
	코드 6-1. if 구문 기본 구현
*/

let first: Int = 5
let second: Int = 7

if first > second {
    print("first > second")
} else if first < second {
    print("first < second")
} else {
    print("first == second")
}


// 결과는 "first < second"가 출력된다.

 

6.1.2 switch 구문

소괄호( () )의 생략 가능. 단, break 키워드 사용은 선택 사항이다. 즉, case 내부의 코드를 모두 실행하면 break 없이도 switch 구문이 종료된다. 스위프트에서 switch 구문의 case를 연속 실행하려면 fallthrough 키워드를 사용한다.

스위프트에서는 switch 구문의 조건에 다양한 값이 들어갈 수 있다. 다만 각 case에 들어갈 비교 값은 입력 값과 데이터 타입이 같아야 한다. 또, 비교될 값이 명확히 한정적인 값(열거형 값 등)이 아닐 때는 default를 꼭 작성해줘야 한다. 또한 각 case에는 범위 연산자를 사용할 수도, where 절을 사용하여 조건을 확장할 수도 있다.

 

/*
	코드 6-3. switch 구문 기본 구현
*/

let integerValue: Int = 5

switch integerValue {
case 0:  // (1)
    print("Value == zero")
case 1...10: // (2)
    print("Value == 1~10")
    fallthrough
case Int.min..<0, 101..<Int.max:   // (3)
    print("Value < 0 or Value > 100")
    break
default:
    print("10 < Value <= 100")
}

// 결과
// Value == 1~10
// Value < 0 or Value > 100

스위프트에서는 (1)처럼 단 하나의 값으로 case를 만들 수 있지만 (2)처럼 범위를 사용할 수도 있다. (3)이 실행된 이유는 앞의 case 블록 (2)에서 fallthrough 키워드를 사용하여 다음 case도 실행되도록 했기 때문이다. 만약 integerValue가 0이라면 "Value == zero"라고 출력 될 것이다.

 

범위 연산자는 정수뿐만 아니라 [코드 6-4]처럼 부동소수 타입에도 사용할 수 있다.

/*
	코드 6-4. 부동소수 타입의 범위 연산을 통한 switch case 구성
*/

let doubleValue: Double = 3.0

switch doubleValue {
case 0:
    print("Value == Zero")
case 1.5...10.5:
    print("1.5 <= Value <= 10.5")
default:
    print("Value == \(doubleValue)")
}

// 1.5 <= Value <= 10.5

 

switch 구문의 입력 값으로 숫자 표현이 아닌 문자, 문자열, 열거형, 튜플, 범위, 패턴이 적용된 타입 등 다양한 타입의 값도 사용 가능하다.

/*
	코드 6-5. 문자열 switch case 구성
*/

let stringValue: String = "Liam Neeson"

switch stringValue {
case "yagom":
    print("He is yagom")
case "Jay":
    print("He is Jay")
case "Jenny", "Joker", "Nova":
    print("He or She is \(stringValue)")
default:
    print("\(stringValue) said 'I don't know who you are'")
}


// Liam Nesson said 'I don't know who you are'"

 

[코드 6-5]의 case "Jenny", "Joker", "Nova": 처럼 여러 개의 항목을 한 번에 case로 지정해주는 것도 가능하다. 그렇지만 case를 연달아 쓰는 것은 불가능하다. case XXX: 다음에는 꼭 실행 가능한 코드가 위치해야 한다.

 

[코드 6-8]처럼 switch 구문의 입력 값으로 튜플도 사용 가능하다.

/*
	코드 6-8. 튜플 switch case 구성
*/

typealias NameAge = (name: String, age: Int)

let tupleValue: NameAge = ("yagom", 99)

switch tupleValue {
case ("yagom", 99):
    print("정답입니다.")
default:
    print("누굴 찾나요?")
}

// 정답입니다.

 

튜플은 이외에도 [코드 6-9]처럼 와일드카드 식별자와 함께 사용하면 좀 더 유용하다. 와일드카드 식별자(_)는 switch 구문 외에도 여러 곳에서 사용된다.

 

/*
	코드 6-9. 와일드카드 식별자를 사용한 튜플 switch case 구성
*/

typealias NameAge = (name: String, age: Int)

let tupleValue: NameAge = ("yagom", 99)

switch tupleValue {
case ("yagom", 50):
    print("정답입니다.")
case ("yagom", _):
    print("이름만 맞췄습니다. 나이는 \(tupleValue.age)입니다.")
case (_, 99):
    print("나이만 맞췄습니다. 이름은 \(tupleValue.name)입니다.")
default:
    print("누굴 찾나요?")
}

// 이름만 맞췄습니다. 나이는 99입니다.

 

그런데 [코드 6-9]처럼 와일드카드 식별자를 사용하면 무시된 값을 직접 가져와야 하는 불편함도 생긴다. 그래서 미리 지정된 조건 값을 제외한 다른 값은 실행문 안으로 가져올 수 있다. 그때 let을 붙인 값 바인딩을 사용한다.

 

/*
	코드 6-10. 값 바인딩을 사용한 튜플 switch case 구성
*/

typealias NameAge = (name: String, age: Int)

let tupleValue: NameAge = ("yagom", 99)

switch tupleValue {
case ("yagom", 50):
    print("정답입니다.")
case ("yagom", let age):
    print("이름만 맞췄습니다. 나이는 \(age)입니다.")
case (let name, 99):
    print("나이만 맞췄습니다. 이름은 \(name)입니다.")
default:
    print("누굴 찾나요?")
}

// 이름만 맞췄습니다. 나이는 99입니다.

 

where 키워드를 사용하여 case의 조건을 확장할 수 있다.

 

/*
	코드 6-11. where를 사용하여 switch case 확장
*/

let position: String = "사원"
let year: Int = 1
let isIntern: Bool = false

switch position {
case "사원" where isIntern == true:
    print("인턴입니다.")
case "사원" where year < 2 && isIntern == false:
    print("신입사원입니다.")
case "사원" where year > 5:
    print("연식 좀 된 사원입니다.")
case "사원":
    print("사원입니다.")
case "대리":
    print("대리입니다.")
default:
    print("직원이 아닙니다.")
}

// 신입사원입니다.

 

열거형과 같이 한정된 범위의 값을 입력 값으로 받게 될 때 값에 대응하는 각 case를 구현한다면 default를 구현하지 않아도 된다. 만약 값에 대응하는 각 case를 구현하지 않는다면 default는 필수이다.

 

/*
	코드 6-12. 열거형을 입력 값으로 받는 switch 구문
*/

enum School {
    case primary, elementary, middle, high
}

let educatation: School = .middle

switch educatation {
case .primary:
    print("최종 학력은 유치원입니다.")
case .elementary:
    print("최종 학력은 초등학교입니다.")
case .middle:
    print("최종 학력은 중학교입니다.")
case .high:
    print("최종 학력은 고등학교입니다.")
}

 

만약 열거형에 case가 추가될 가능성이 있다면 switch 구문에서는 어떻게 대비해야 할까?

switch 구문이 모든 case에 대비하기 위해 스위프트 5.0 버전에서 추가된 unknown 이라는 속성을 이용하면 이런 문제를 조금 유려하게 대처할 수 있다.

 

/*
	코드 6-13. Menu 열거형의 모든 case를 처리하는 switch 구문의 상태
*/

enum Menu {
    case chicken
    case pizza
}

let lunchMenu: Menu = .chicken

switch lunchMenu {
case .chicken:
    print("치킨입니다.")
case .pizza:
    print("피자입니다.")
case _: // case default: 와 같은 표현이다.
    print("메뉴가 뭐죠?")
}

 

[코드 6-13]의 Menu라는 열거형에 case가 추가될 것을 대비해 switch 구문의 마지막 case로 와일드카드 case(case _)를 미리 추가했다. 그러면 나중에 Menu 열거형에 case를 추가해도 switch 구문에서 컴파일 오류가 발생하지 않을것이다.

하지만 미래에 Menu 열거형에 새로운 case를 추가했지만 switch 구문의 내부 코드를 수정하지 않았다면 문법적으로는 오류가 없지만 논리적 오류가 발생할 수 있는 여지가 생긴다. 이런 문제를 방지하기 위해 unknown 속성을 사용할 수 있다.

 

/*
	코드 6-14. 차후에 Menu 열거형에 추가한 case를 처리하지 않으면 경고를 내어줄 unknown 속성
*/

enum Menu {
    case chicken
    case pizza
    case hamburger
}

let lunchMenu: Menu = .chicken

switch lunchMenu {
case .chicken:
    print("치킨입니다.")
case .pizza:
    print("피자입니다.")
@unknown case _:
    print("메뉴가 뭐죠?")
}

 

[코드6-14]에서는 Menu 열거형에 hamburger case를 추가했고, case _: 앞에 unknown 속성을 붙였다. unknown 속성을 부여하면 case _ 에서 아래 그림과 같이 경고가 발생한다.

 

 

이를 통해 switch 구문이 모든 case에 대응하지 않는다는 사실을 다시 상시할 수 있다.

이처럼 논리적인 오류에 대해 도움을 받을 수 있는 unknown 속성을 부여할 수 있는 case는 case _ 혹은 default case 뿐이다. 또, unknown 속성을 부여한 case는 switch 구문의 가장 마지막 case로 작성해야 한다.

 

6.2 반복문


6.2.1 for-in 구문

for-in 반복 구문은 반복적인 데이터나 시퀀스를 다룰 때 많이 사용된다.

 

for 임시 상수 in 시퀀스 아이템 {
    실행 코드
}

 

for-in 구문은 스위프트의 기본 컬렉션 타입에서도 유용하게 사용할 수 있다. 딕셔너리는 넘겨받는 값의 타입이 튜플로 지정되어 넘어온다.

 

/*
	코드 6-16. 기본 데이터 타입의 for-in 반복 구문 사용
*/

// Dictionary
let friends: [String: Int] = ["Jay": 35, "Joe": 29, "Jenny": 31]

for tuple in friends {
    print(tuple)
}

// ("Joe", 29)
// ("Jay", 35)
// ("Jenny", 31)

for (key, value) in friends {
    print("\(key) : \(value)")
}

// Set
let 지역번호: Set<String> = ["02", "031", "032"]

for 번호 in 지역번호 {
    print(번호)
}

// 02
// 031
// 032

 

6.2.3 repeat-while 구문

repeat 블록의 코드를 최초 1회 실행한 후, while 다음의 조건이 성립하면 블록 내부의 코드를 반복 실행한다.

 

/*
	코드 6-18. repeat-while 반복 구문의 사용
*/

var names: [String] = ["John", "Jenny", "Joe", "Choi"]

repeat {
    print("Good bye \(names.removeFirst())")
    // removeFirst()는 요소를 삭제함과 동시에 삭제한 요소를 반환한다.
} while names.isEmpty == false

// Good bye John
// Good bye Jenny
// Good bye Joe
// Good bye Choi

 

 

6.3 구문 이름표


반복문을 작성하다 보면 종종 반복문을 중첩으로 사용하게 된다. 이때 반복문을 제어하는 키워드(break, continue 등)가 어떤 범위에 적용되어야 하는지 애매하거나 큰 범위의 반복문을 종료하고 싶은데 작은 범위의 반복문만 종료되는 등 예상치 못한 실수를 할 수도 있다. 그럴 때는 반복문 앞에 이름과 함께 콜론을 붙여 구문의 이름을 지정해주는 구문 이름표를 사용하면 좋다. 이름이 지정된 구문을 제어하고자 할 때는 제어 키워드와 구문 이름을 함께 써주면 된다.

 

/*
	코드 6-19. 중첩된 반복문의 구문 이름표 사용
*/

var numbers: [Int] = [3, 2342, 6, 3252]

numbersLoop: for num in numbers {
    if num > 5 || num < 1 {
        continue numbersLoop
    }
    
    var count: Int = 0
    
    printLoop: while true {
        print(num)
        count += 1
        
        if count == num {
            break printLoop
        }
    }
    
    removeLoop: while true {
        if numbers.first != num {
            break numbersLoop
        }
        numbers.removeFirst()
    }
}


// 3
// 3
// 3
// numbers에는 [2342, 6, 3253]가 남는다.

 

 

 

스위프트 프로그래밍: Swift 5 - 교보문고

객체지향, 함수형, 프로토콜 지향 패러다임까지 한 번에! | 스위프트를 제대로 이해하고 싶은 개발자를 위한 책스위프트는 iOS와 macOS용 애플리케이션 개발에 주로 사용하는 프로그래밍 언어입니

www.kyobobook.co.kr

 

반응형

'iOS > SWIFT' 카테고리의 다른 글

[Swift] Chapter 08. 옵셔널  (0) 2021.10.08
[Swift] Chapter 07. 함수  (0) 2021.10.08
[Swift] Chapter 05. 연산자  (0) 2021.10.08
[Swift] Chapter 04. 데이터 타입 고급  (0) 2021.10.08
[Swift] Chapter 03. 데이터 타입 기본  (0) 2021.10.08

댓글