where 절

피터·2022년 9월 30일
0
post-thumbnail

where 절의 활용

  • 패턴과 결합하여 조건 추가
    • 특정 패턴에 Bool 타입 조건을 지정하는 것
  • 타입에 대한 제약 추가
    • 어떤 타입이 특정 프로토콜 준수 조건을 추가
let tupleValues = [(1, 2), (1, -1), (1, 0), (0, 2)]

for tupleValue in tupleValues {
    switch tupleValue {
    case let (x, y) where x == y: print("x == y")
    case let (x, y) where x == -y: print("x == -y")
    case let (x, y) where x > y: print("x > y")
    case (1, _) : print("x == 1")
    case (_, 2) : print("y == 2")
    default: print(tupleValue)
    }
}

// x == 1
// x == -y
// x > y
// y == 2

var repeatCount: Int = 0

for tuple in tupleValues {
    switch tuple {
    case let (x, y) where x == y && repeatCount > 2: print("x == y")
    case let (x, y) where repeatCount < 2: print("\(x), \(y)")
    default: print("아무것도 아님")
    }
    
    repeatCount += 1
}

// 1, 2
// 1, -1
// 아무것도 아님
// 아무것도 아님
  • where 절은 옵셔널 패턴과도 결합할 수 있습니다.
let arrayOptionalInts: [Int?] = [2, 4, nil, 1, 5, nil]

for case let number? in arrayOptionalInts where number > 2 {
    print("2보다 큰 정수를 찾아라! \(number)")
}

// 2보다 큰 정수를 찾아라! 4
// 2보다 큰 정수를 찾아라! 5
  • where 절은 타입캐스팅 패턴과 결합할 수 있습니다.
let anyValue: Any = "가나다"

switch anyValue {
case let value where value is Int: print("\(value)는 Integer입니다.")
case let value where value is String: print("\(value)는 String입니다.")
case let value where value is Double: print("\(value)는 Double입니다.")
default: print("알 수 없는 타입입니다.")
}

// 가나다는 String입니다.
let things: [Any] = [1, 2.3, "가나다", 4, 9.84, "와우우", 0, 0.0]

for thing in things {
    switch thing {
    case 0 as Int: print("0은 Int처럼 존재한다")
    case 0.0 as Double: print("0.0은 Double처럼 존재한다.")
    case let someInt as Int: print("\(someInt) 정수입니다.")
    case let someDouble as Double: print("\(someDouble) Double입니다.")
    default: print("암것도 아닙니다.")
    }
}

// 1 정수입니다.
// 2.3 Double입니다.
// 암것도 아닙니다.
// 4 정수입니다.
// 9.84 Double입니다.
// 암것도 아닙니다.
// 0은 Int처럼 존재한다
// 0.0은 Double처럼 존재한다.
  • 프로토콜 익스텐션에 where 절을 사용하면 이 익스텐션이 특정 프로토콜을 준수하는 타입에만 적용될 수 있도록 제약을 줄 수 있습니다. 다시 말해 익스텐션이 적용된 프로토콜을 준수하는 타입 중 where 절 뒤에 제시되는 프로토콜도 준수하는 타입만 익스텐션이 적용되도록 제약을 줄 수 있다는 뜻입니다.
protocol SelfPrintable {
    func printSelf()
}

struct Person: SelfPrintable {
    
}

extension Int: SelfPrintable { }
extension String: SelfPrintable { }
extension UInt: SelfPrintable { }
extension Double: SelfPrintable { }

extension SelfPrintable where Self: FixedWidthInteger, Self: SignedInteger {
    func printSelf() {
        print("이 타입은 FixedWidthInteger와 SignedInteger를 준수하면서 SelfPrintable를 준수한 타입\(type(of: self))입니다.")
    }
}

extension SelfPrintable where Self: CustomStringConvertible {
    func printSelf() {
        print("이 타입은 CustomStringConvertible를 준수하면서 SelfPrintable를 준수한 타입\(type(of: self))입니다.")
    }
}

extension SelfPrintable {
    func printSelf() {
        print("그 외 SelfPrintable를 준수하는 타입 \(type(of: self))")
    }
}

Int(-9).printSelf()
// 이 타입은 FixedWidthInteger와 SignedInteger를 준수하면서 SelfPrintable를 준수한 타입Int입니다.
UInt(8).printSelf()
// 이 타입은 CustomStringConvertible를 준수하면서 SelfPrintable를 준수한 타입UInt입니다.
String("Peter").printSelf()
// 이 타입은 CustomStringConvertible를 준수하면서 SelfPrintable를 준수한 타입String입니다.
Double(8.0).printSelf()
// 이 타입은 CustomStringConvertible를 준수하면서 SelfPrintable를 준수한 타입Double입니다.
Person().printSelf()
// 그 외 SelfPrintable를 준수하는 타입 Person
  • 타입 매개변수와 연관 타입의 제약을 추가하는 데 where 절을 사용하기도 합니다. 제네릭 메서드의 반환 타입 뒤에 where 절을 포함하면 타입 매개변수와 연관 타입에 요구사항을 추가할 수 있습니다. 요구사항이 여러 개일 때는 쉼표로 구분합니다.
func double<T>(integerValue: T) -> T where T: BinaryInteger {
    return integerValue * 2
}

func compareTwoValue<S1, S2>(_ a: S1, _ b: S2) where S1: Sequence, S2: Sequence, S1.Element == S2.Element {
    ...
}

protocol Container {
    associatedtype Item where Item: BinaryInteger
    
    var count: Int { get }
    mutating func append(_ item: Item)
}
  • 연관 타입이 특정 프로토콜을 준수하는 경우에만 제네릭 타입에 프로토콜을 채택하도록 재네릭 타입의 연관 타입에 제약을 줄 수 있습니다.
protocol Talkable { }
protocol CallToAll {
    func callToAll()
}

struct Person: Talkable { }
struct Animal { }

extension Array: CallToAll where Element: Talkable {
    func callToAll() {
        print("여러부운")
    }
}

let people: [Person] = []
let animals: [Animal] = []

people.callToAll()
// 여러부운
animals.callToAll() // 컴파일 오류 

이와 같이 where 절은 다른 패턴과 조합하면 원하는 추가 요구사항을 자유롭게 더할 수 있으며, 익스텐션과 제네릭에 사용함으로써 프로토콜 또는 타입에 대한 제약을 추가해줄 수도 있습니다. 조건 구문이나 논리 연산으로 구현한 코드보다는 훨씬 명확하고 간편하게 사용할 수 있습니다.

자료 출처: 야곰 스위프트 프로그래밍 3판

"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."
profile
iOS 개발자입니다.

0개의 댓글