[Swift] where절

이정훈·2023년 6월 30일
0

Swift 파헤치기

목록 보기
5/10
post-thumbnail

이번 포스트에서는 Swift에서 조건을 추가할때 사용하는 where 문법에 대해 알아보려고 한다.

where절의 용도

where이라는 표현은 사실 SQL을 사용해 봤다면 특정 레코드의 조건을 만족하는 데이터에 대해 SELECT, UPDATE 등의 명령을 수행하기 위해 사용한 경험이 있을 수 있다.

SELECT * FROM CLIENT WHERE AGE > 20;

Swift에서도 비슷한 흐름으로 조건을 추가하거나, 타입에 대한 제한을 가하기 위해 where 문법을 사용할 수 있다.

구체적으로 다시 설명하자면 Swift에서는 패턴 매칭에 조건을 추가하거나, 제네릭이나 프로토콜 익스텐션 등에서 타입에 제약을 주기 위해 사용할 수 있다.


패턴에서 where절

먼저 패턴에서 사용되는 where 절의 형태를 살펴보자

아래의 코드는 Tuple Pattern과 함께 where절을 사용하여 포인트 좌표가 몇 사분면에 위치하는지 조건에 따라 출력하는 예제이다.

let point: (Int, Int) = (-3, 2)

switch point {
case let (x, y) where x > 0 && y > 0:
    print("(\(x), \(y)): 1사분면")
case let (x, y) where x < 0 && y > 0:
    print("(\(x), \(y)): 2사분면")
case let (x, y) where x < 0 && y < 0:
    print("(\(x), \(y)): 3사분면")
case let (x, y) where x > 0 && y < 0:
    print("(\(x), \(y)): 4사분면")
case let (_, _):
    print("(\(point.0), \(point.1)): 선의 경계")
}
//(-3, 2): 2사분면

Tuple Pattern으로 매칭 후 where절을 사용하여 조건을 추가 함으로서 조건에 해당하는 case문을 실행할 수 있다.

추가적으로 Optional Pattern에서 사용되는 where절 예시를 한가지만 더 살펴보자

let heights: [Int?] = [nil, 178, 163, 172, nil, 181, nil]

for case .some(let value) in heights where value > 170 {
    print(value)
}

//178
//172
//181

Optional Pattern으로 값이 nil이 아닌 경우, 추가적으로 where절로 조건을 통해 그 값이 170보다 큰 경우 해당 값을 출력하도록 제약을 줄 수 있다.

이외에도 Swift의 다양한 패턴where절을 사용할 수 있다.


protocol extension

프로토콜 익스텐션where절을 사용하면 프로토콜을 준수하는 타입 중 특정 프로토콜을 함께 준수하는 타입만 익스텐션으로 기능을 확장 하도록 제약을 줄 수 있다.

코드로 표현하자면 다음과 같다.

protocol A {}

extension A where Self: B, Self: C {}

위 코드의 의미는 프로토콜 A를 준수하는 타입에서 B 프로토콜과 C 프로토콜을 함께 준수하는 타입에 한해서 extesion으로 기능을 확장한다는 의미이다.

아래의 코드에서 PrintCharacter 프로토콜에 익스텐션을 사용하여 CustomStringConvertible 프로토콜과 Collection 프로토콜을 동시에 준수하는 타입에 대해서 문자열의 각 문자를 하나씩 출력 하도록 하는 함수를 추가 하도록하는 예제이다.

protocol PrintCharacter {
    func printCharacter()
}

extension String: PrintCharacter {}
//extension Int: PrintCharacter {}

extension PrintCharacter where Self: CustomStringConvertible, Self: Collection {
    func printCharacter() {
        for char in description {
            print(char)
        }
    }
}

String("Hello").printCharacter()
//H
//e
//l
//l
//o
//Int(19).printCharacter()

Int 타입은 CustomStringConvertible 프로토콜과 Collection 프로토콜을 동시에 준수하지 않기 때문에 Int 타입에 적용하려고 하면 에러가 발생한다.


제네릭에서 where절

제네릭에서 타입 매개변수연관 타입where절을 사용하여 조건을 추가할 수 있다.

아래의 코드는 제네릭타입 매개변수where절을 사용하여 조건을 추가하는 예제이다.

func compareTwoItems<T1, T2>(first: T1, second: T2) where T1: Comparable, T2: Comparable, T1 == T2 {
    if first > second {
        print("\(first)(이)가 \(second)보다 큽니다.")
    } else if first < second {
        print("\(second)(이)가 \(first)보다 큽니다.")
    } else {
        print("두 값이 같습니다.")
    }
}

compareTwoItems(first: 3, second: 5)    //5(이)가 3보다 큽니다.
compareTwoItems(first: "b", second: "a")    //b(이)가 a보다 큽니다.

함수 compareTwoItems타입 매개변T1, T2를 가지며 where절을 사용하여 T1T2Comparable 프로토콜을 준수하는 타입이면서 두 타입 매개변수가 같아야 한다는 제약을 추가하였다.

따라서 Comparable 프로토콜을 준수하는 Int, Double, String, Date 등의 값을 매개변수로 전달하여 함수를 호출할 수 있다.

아래 코드는 프로토콜에서 사용하는 연관 타입where절을 사용하여 조건을 추가하는 예제이다.

protocol IntegerStack {
    associatedtype ItemType where ItemType: BinaryInteger
    
    var items: [ItemType] {get}
    mutating func push(item: ItemType) -> Void
    mutating func pop() -> ItemType
    
    subscript(i: Int) -> ItemType {get}
    subscript<Indices: Sequence>(indices: Indices) -> [ItemType] where Indices.Iterator.Element == Int {get}
}

연관 타입 ItemTypeBinaryInteger 프로토콜을 준수하는 타입으로 Int, UInt 등의 타입이 올 수 있다.

profile
새롭게 알게된 것을 기록하는 공간

0개의 댓글