상세 컨텐츠

본문 제목

[Day4] 흐름 제어 구문(2) : Conditional Statements(Branch Statements)

Swift&SwiftUI

by (방울)도마토 2024. 4. 9. 23:33

본문

조건문 

- 프로그램에서 하나 또는 그 이상의 조건값에 따라 특정 구문을 시행하도록 프로그램의 흐름을 분기하는 역할 

 

 

if 문

1. if 구문

if <조건식> {
    <실행할 구문>
}

 

- 조건문은 반드시 Bool 타입의 true or false을 판단할 수 있는 형태의 구문 

- 조건문이 true일 때, 코드 블록 내부의 구문이 실행됨

var adult = 19
var age = 15

if age < adult {
    print("당신은 미성년자입니다")
} // 당신은 미성년자입니다

 

 

2. if ~ else 구문

- 조건식이 true일 때는 A 구문을, true이 아닐 때는 B 구문을 실행

if <조건식> {
    <A 구문>
} else {
    <B 구문>
}


- else 구문은 조건식이 참이 아닌 모든 경우 실행 

- if block과 else block 둘 중 하나는 반드시 실행 

    - 이율배반 : 두 코드 블록이 모두 실행되는 경우는 있을 수 없음 

let adult = 19
var age = 20 

if age < adult {
    print("당신은 미성년자입니다.")
} else {
    print("당신은 성년자입니다.")
} // 당신은 성년자입니다.

 

 

if 구문의 중첩

var adult = 19
var age = 21
var gender = "W"

if adult > age {
    if gender = "W" {
        print("여성 미성년자입니다")
    } else {
        print("남성 미성년자입니다")
    }
} else {
    if gender == "W" {
        print("여성 성년자입니다")
    } else {
        print("남성 성년자입니다")
    }
} // 여성 성년자입니다

 

- 3단계 이상의 중첩 구문은 사용하지 않는 것이 좋음 

 

3. if ~ else if 구문

- 비교할 조건이 여러 개일 경우  

- 단순히 true or false로만 판단하기 어려운 여러 개의 조건이 있을 때 

- else if 구문은 else와 달리 여러 번 사용 가능 

if <조건1> {
    <조건1이 true일 때 실행될 구문>
} else if <조건2> {
    <조건2이 true일 때 실행될 구문>
} else {
    <앞의 조건들을 전부 만족하지 않았을 때 실행할 구문>
}

 

if gender == "W" {
    print("여성 성년자입니다")
} else if {
    print("남성 성년자입니다")
} else {
    print("어느 쪽에도 속하지 않습니다")
}

 

cf. else if 구문을 if 구문으로 대체할 수 있나요?

- 컴파일러는 if ~ else if 구문을 하나의 조건식으로 인식하지만, if ~ if 구문은 서로 별개의 조건문으로 인식 

    - if ~ else if 구문에서는 차례대로 조건식을 비교하다가 일치하는 것이 발견되면 더이상 비교를 진행하지 않고 조건식을 종료

    - if ~ if 구문에서는 이미 일치하는 조건식을 발견하여도 이후의 모든 조건식을 비교한 후 구문을 마침

 


guard 구문

- 전체 구문을 조기 종료(Early Exit)하기 위한 목적으로 사용 

- 후속 코드들이 실행되기 전에 특정 조건을 만족하는지 확인하는 용도로 사용 

guard <조건식 또는 표현식> else {
    <조건식 또는 표현식의 결과가 false일 때 실행될 코드>
}

 

예) Divide By Zero

// 입력받은 값을 이용하여 100으로 나누는 함수 
func divide(base: Int) {
    
    // Divide by Zero 방지(0으로 나눌 경우 오류 발생)
    guard base != 0 else {
        print("연산할 수 없습니다")
        return // 함수 종료
    }
    
    // base 값이 0이 아닐 경우 코드 실행 
    let result = 100 / base
    print(result)
}

 

- guard 구문은 if 구문으로 대체할 수 있음 

func divide(base: Int) {
   
    // guard 구문의 조건식과 반대로 바뀜
    if base == 0 {
        print("연산을 처리할 수 있습니다")
        return
    }
    
    let result = 100 / base
    print(result)
}

 

- guard 구문은 실행 흐름을 종료하기 위한 목적으로 사용되는 구문이므로 코드를 중첩해서 사용하지 않아도 됨 

    - 전체 코드를 굉장히 깔끔하고 단순하게 만들어줌 

- 조건을 체크하여 실행 흐름을 종료시킬 때는 가급적 guard 구문을 사용하는 것이 좋음

    - 실행 흐름을 이어나가고 싶은 경우에만 if ~ else 구문을 사용하는 것이 좋음 

 


#available

- OS version 별로 구문을 분리해야 할 때 사용 

if #available(<플랫폼이름 버전>, <...>, <*>) {
    <해당 버전에서 사용할 수 있는 API 구문>
} else {
    <API를 사용할 수 없는 환경에 대한 처리>
}
// 플랫폼 이름과 버전 사이는 공백으로 구분, 입력 개수는 제한이 없음
// 마지막은 *로 마감
if #available(iOS 17, macOS 14, watchOS 6, *) {
    // iOS 17용 API 구문 또는 macOS 14 구문, watchOS 6용 API 구문 
} else {
    // API를 사용하지 못했을 때에 대한 실패 처리
}

 


switch 구문

- 입력받은 값을 패턴으로 비교하고 그 결과를 바탕으로 실행 블록을 결정하는 조건문 

- 나열된 패턴들을 순서대로 비교하다가 일치하는 첫 번째 패턴의 코드블록 실행 

- 다양한 가능성이 있는 여러 개의 조건 비교에 효율적으로 대응하기에는 조금 부족한 if 구문의 대안 

 

switch <비교 대상> {
    case <비교 패턴1> :
        <비교 패턴1이 일치했을 때 실행할 구문>
    case <비교 패턴2>, <비교 패턴3> :
        <비교 패턴2 또는 비교 패턴3이 일치했을 때 실행할 구문>
    default :
        <어느 비교 패턴과도 일치하지 않았을 때 실행할 구문>
}

 

- swift의 switch 구문은 일치하는 비교 패턴이 있을 경우 해당 블록의 실행 코드를 처리하고, 더이상의 비교 없이 전체 분기문 종료 

- 오직 하나의 case 구문만 처리하고 나면 더이상 비교 진행 X → break 구문 생략 

    - C나 Java에서는 비교 패턴이 일치할 경우 우선 실행 구문을 처리한 다음, 나머지 case에 대한 비교를 계속 진행 

    - 추가로 일치하는 패턴이 있다면 이를 모두 실행하고, 마지막 case를 비교한 후에야 분기문 종료

 

let val = 2

switch val {
    case 1 :
        print("일치한 값은 1입니다")
    case 2 :
        print("일치한 값은 2입니다")
    case 2 :
        print("일치한 값 2가 더 있습니다")
    default :
        print("일치한 값이 없습니다")
} // 일치한 값은 2입니다

// ------------------------------
// 다른 언어에서의 실행 결과 (break 문이 없음)
    // 일치한 값은 2입니다
    // 일치한 값 2가 더 있습니다
    // 일치한 값이 없습니다

 

 

암시적인 Fall Through

- 패턴이 일치하는 case 블록을 실행하는 대신, 그 다음 case 블록으로 실행 흐름을 전달하는 문법 

- 명시해주지 않아도 된다는 점에서 암시적인 Fall Through라고 함

// C언어 계열
let sampleChar : Character = "a"

switch sampleChar {
    case "a" : 
    case "A" :
        print("글자는 A입니다")
    default : 
    	print("일치하는 글자가 없습니다")
} // 글자는 A입니다

- 암시적인 Fall Through 가 적용되면 실행 흐름이 전달된 비교 블록은 패턴 일치 여부에 상관없이 실행 블록 처리 

    - 4행의 case 문과 일치하지만 6행이 실행됨

 

* swift에선 암시적 Fall Through를 지원하지 않음 : 명시적으로 fallthrough 구문 사용

let sampleChar : Character = "a"

switch sampleChar {
    case "a" : 
        fallthrough
        // 해당 블록은 비교 패턴이 일치할 경우 인접한 case 블록으로 실행 흐름 전달 
        // 실행 흐름을 전달받은 case 블록은 비교 패터의 일치 여부와 상관없이 작성된 구문 실행 후 
        // switch 구문 종료 
    case "A" :
        print("글자는 A입니다")
    default : 
    	print("일치하는 글자가 없습니다")
} // 글자는 A입니다

 

 

switch 구문의 특성 

- switch 구문에서 사용된 비교 대상은 반드시 하나의 비교 패턴과 일치해야 함 

    - 비교 대상이 비교 패턴 중 어느 것과도 일치하지 않아 분기문 내의 어떤 블록도 실행되지 못하는 경우를 switch 구문이 fail 했다고 부름 

- 모든 case 구문에서 일치된 패턴을 찾지 못했을 경우에 대비하여 switch 구문에는 반드시 default 구문을 추가해야 함

 

- 하나의 case 키워드 다음에 하나 이상의 비교 패턴을 연이어 작성할 수 있음

var value = 3

switch value {
    case 0, 1 :
        print("0 또는 1입니다")
    case 2, 3 :
        print("2 또는 3입니다")
    default :
        print("default 입니다")
} // 2 또는 3입니다

 

- 튜플 내부의 아이템이 비교 대상과 부분적으로 일치할 경우, case 비교 패턴 전체가 일치하는 것으로 간주 

    - 이 때, 일치하지 않는 나머지 부분을 상수나 변수화 하여 사용 가능 

var value = (2, 3)

switch value {
    case let (x, 3) :
        print("튜플의 두번째 값이 3일 때 첫 번째 값은 \(x)입니다.")
    case let (3, y) :
        print("튜플의 첫번째 값이 3일 때 두 번째 값은 \(y)입니다.")
    case let (x, y) :
        print("튜플의 값은 각각 \(x), \(y)입니다.")
} // 튜플의 두번째 값이 3일 때 첫 번째 값은 2입니다.

- 위 예의 패턴 비교 구문 세 개는 모두 성립함 

    - (x, 3)은 (2, 3)과 부분적으로 일치함 → 일치하지 않는 첫 번째 아이템을 변수로 처리하면 구문의 비교 조건을 만족시킴 

        - 변수 x는 필요한 것에 사용 가능 

    - (3, y)은 (2, 3)과 부분적으로 일치함 → 일치하지 않는 두 번째 아이템을 변수로 처리하면 구문의 비교 조건을 만족시킴 

    - (x, y)은 (2, 3)과 패턴적으로 일치함 → 변수로 처리된 부분은 '어떤 값이든 들어올 수 있다'는 의미

 

 

- 범위 연산자를 사용하여 해당 범위에 속하는 값을 매칭할 수 있음 (Interval Matching)

var passtime = 1957

switch passtime {
    case 0..<60 : 
        print("방금 작성된 글입니다")
    case 60..<3600 :
    	print("조금 전 작성된 글입니다")
    case 3600..<86400 :
    	print("얼마 전 작성된 글입니다")
    default :
    	print("예전에 작성된 글입니다")
} // 조금 전 작성된 글입니다
var value = (2, 3)

switch value {
    case (0..<2, 3) :
        print("범위 A에 포함되었습니다")
    case (2..<5, 0..<3) :
        print("범위 B에 포함되었습니다")
    case (2..<5, 3..<5) :
        print("범위 C에 포함되었습니다")
    default :
        print("범위 D에 포함되었습니다")
} // 범위 C에 포함되었습니다

 

- where 구문을 추가하여 각 case 블록 별로 복잡한 패턴까지 확장하여 매칭할 수 있음 

var point = (3, -3)

switch point {
    case let (x, y) where x == y :
        print("\(x)와 \(y)은 x==y 선 상에 있습니다")
    case let (x, y) where x == -y :
        print("\(x)와 \(y)은 x==-y 선 상에 있습니다")
    case let (x, y) where x == -y :
        print("\(x)와 \(y)은 일반 좌표상에 있습니다")
} // 3와 -3은 x==-y 선 상에 있습니다"

- point 변수는 switch 구문의 case 블록에서 각각 x, y로 할당

- 해당 임시 변수들은 다시 where 구문에서 조건 비교에 사용 

관련글 더보기

댓글 영역