xho95 (소중한꿈)'s Swift Life

Apple 에서 공개한 The Swift Programming Language (Swift 5.7) 책의 Patterns 부분1을 번역하고, 설명이 필요한 부분은 주석을 달아서 정리한 글입니다. 전체 번역은 Swift 5.7: Swift Programming Language (스위프트 프로그래밍 언어) 에서 확인할 수 있습니다.

Patterns (패턴)

패턴 (pattern) 은 단일 값 또는 합성 값의 구조를 나타냅니다. 예를 들어, 튜플 (1, 2) 의 구조는 쉼표로-구분한 두 원소의 목록입니다. 패턴은 어떤 특별한 값 보단 값의 구조를 나타내기 때문에, 다양한 값과 맞춰볼 수 있습니다. 구체적인 사례로, (x, y) 라는 패턴은 튜플 (1, 2) 및 어떤 다른 두-원소의 튜플과도 맞습니다. 패턴을 값과 맞춰보는 것에 더해, 합성 값의 일부 또는 전체를 뽑아내서 각 부분을 상수나 변수 이름에 연결할 수도 있습니다.

스위프트에는, 두 종류의 기본 패턴이 있는데: 어떤 종류의 값이든 성공적으로 맞춰보는 것과, 지정한 값과 맞춰보는게 실행 시간에 실패할 수도 있는 것이 그것입니다.

첫 번째 종류의 패턴은 단순 변수와, 상수, 및 옵셔널 연결 안의 값을 해체하는 (destructuring) 데 사용합니다. 이는 와일드카드 패턴과, 식별자 패턴, 및 이들을 담은 어떤 값 연결이나 튜플 패턴이든 포함합니다. 이러한 패턴에 타입 보조 설명을 지정하면 특정 타입 값하고만 맞춰보도록 이들을 구속할 수 있습니다.

두 번째 종류의 패턴은 전체 패턴 맞춤 (full pattern matching) 에 사용하는데, 여기선 맞춰보려는 값이 실행 시간에 없을 수도 있습니다. 이는 열거체 case 패턴과, 옵셔널 패턴, 표현식 패턴, 및 타입-변환 패턴을 포함합니다. 이러한 패턴은 switch 문의 case 이름표나, do 문의 catch 절, 또는 if 나, while, guard, 또는 for-in 문의 조건에 사용합니다.

GRAMMAR OF A PATTERN 부분 생략 - 링크

Wildcard Pattern (와일드카드 패턴)

와일드카드 패턴 (wildcard pattern) 은 어떤 값과도 맞은 다음 (이를) 무시하며 밑줄 (_) 로 구성합니다. 맞춰볼을 값에 신경 쓰지 않을 때 와일드카드 패턴을 사용합니다. 예를 들어, 다음 코드는 닫힌 범위 1...3 을 반복하면서, 반복문 각 회차의 현재 범위 값을 무시합니다:

for _ in 1...3 {
  // 어떤 걸 세 번 반복합니다.
}

GRAMMAR OF A WILD PATTERN 부분 생략 - 링크

Identifier Pattern (식별자 패턴)

식별자 패턴 (identifer pattern) 은 어떤 값과도 맞으며 그 맞은 값을 변수나 상수 이름에 연결합니다. 예를 들어, 다음 상수 선언의, someValue 라는 식별자 패턴은 Int 타입 값 42 와 맞습니다:

let someValue = 42

맞춤이 성공할 땐, 42 값을 someValue 라는 상수 이름에 연결 (할당) 합니다.

변수나 상수 선언의 왼쪽 패턴이 식별자 패턴일 땐, 식별자 패턴은 암시적으로 값-연결 패턴의 하위 패턴입니다.

GRAMMAR OF A IDENTIFIER PATTERN 부분 생략 - 링크

Value-Binding Pattern (값-연결 패턴)

값-연결 패턴 (value-binding pattern) 은 맞은 값을 변수나 상수 이름에 연결합니다. 맞은 값을 상수 이름에 연결하는 값-연결 패턴은 let 키워드로 시작하며; 변수 이름에 연결하는 건 var 키워드로 시작합니다.

값-연결 패턴 안에 있는 식별자 패턴은 새로 이름의 변수나 상수를 자신과 맞는 값에 연결합니다. 예를 들어, 튜플 원소를 분해하여 각 원소 값을 해당하는 식별자 패턴에 연결할 수 있습니다.

let point = (3, 2)
switch point {
  // x 와 y 를 point 원소에 연결함.
case let (x, y):
  print("The point is at (\(x), \(y)).")
}
// "The point is at (3, 2)." 를 인쇄함

위 예제에선, let(x, y) 튜플 패턴의 각 식별자 패턴으로 배포합니다.2 이런 동작 때문에, case let (x, y):case (let x, let y): 라는 switch 문 case 에 맞는 값은 똑같습니다.

GRAMMAR OF A VALUE-BINDING PATTERN 부분 생략 - 링크

Tuple Pattern (튜플 패턴)

튜플 패턴 (tuple pattern) 은 쉼표로-구분한 0 개 이상의 패턴 목록을, 괄호로 테두리 친 것입니다. 튜플 패턴은 해당하는 튜플 타입의 값과 맞춰봅니다.

튜플 타입을 구속하여 특정 종류의 튜플 타입과 맞춰보려면 타입 보조 설명을 사용하면 됩니다. 예를 들어, 상수 선언 let (x, y): (Int, Int) = (1, 2) 안의 (x, y): (Int, Int) 라는 튜플 패턴은 그 안의 원소가 둘 다 Int 일 때만 맞춰봅니다.

튜플 패턴을 for-in 문 안의 패턴이나 변수 또는 상수 선언 안의 패턴으로 사용할 땐, 와일드카드 패턴이나, 식별자 패턴, 옵셔널 패턴, 또는 이를 담은 다른 튜플 패턴만을 담을 수 있습니다. 예를 들어, 다음 코드는 유효하지 않은데 튜플 패턴 (x, 0) 안의 원소 0 이 표현식 패턴이기 때문입니다:3

let points = [(0, 0), (1, 0), (1, 1), (2, 0), (2, 1)]
// 이 코드는 유효하지 않습니다.
for (x, 0) in points {
  /* ... */
}

단일 원소만 담은 튜플 패턴 주변의 괄호는 아무런 효과가 없습니다. 패턴은 그 단일 원소 타입인 값과 맞춰봅니다. 예를 들어, 다음은 서로 같은 겁니다:

let a = 2        // a: Int = 2
let (a) = 2      // a: Int = 2
let (a): Int = 2 // a: Int = 2

GRAMMAR OF A TUPLE PATTERN 부분 생략 - 링크

Enumeration Case Pattern (열거체 case 패턴)

열거체 case 패턴 (enumeration case pattern) 은 기존 열거체 타입의 case 와 맞춰봅니다. 열거체 case 패턴은 switch 문의 case 이름표 안에서 그리고 if 와, while, guard, 및 for-in 문의 조건 안에서 나타납니다.

맞춰보려는 열거체 case 값에 어떤 결합 값이 있으면, 해당하는 열거체 case 패턴에도 반드시 각각의 결합 값마다 한 원소씩 담은 튜플 패턴을 지정해야 합니다. switch 문을 써서 결합 값을 담은 열거체 case 와 맞춰보는 예제는, Associated Values (결합 값) 부분을 보기 바랍니다.

열거체 case 패턴은 그 case 를 옵셔널 안에 포장한 값과도 맞춰봅니다. 이 단순해진 구문은 옵셔널 패턴을 생략하게 합니다. Optional 은 열거체로 구현하기 때문에, 똑같은 swift 문 안에 .none.some 이 열거체 타입의 case 로 나타날 수 있다는 걸 기억하기 바랍니다.

enum SomeEnum { case left, right }
let x: SomeEnum? = .left
switch x {
case .left:
  print("Turn left")
case .right:
  print("Turn right")
case nil:
  print("Keep going straight")
}
// "Turn left" 를 인쇄함

GRAMMAR OF AN ENUMERATION CASE PATTERN 부분 생략 - 링크

Optional Pattern (옵셔널 패턴)

옵셔널 패턴 (optional pattern)Optional<Wrapped> 열거체의 some(Wrapped) case 안에 포장한 값과 맞춰봅니다. 옵셔널 패턴은 식별자 패턴과 그 바로 뒤의 물음표로 구성하며 열거체 case 패턴과 동일한 곳에 나타납니다.

옵셔널 패턴은 Optional 열거체 case 패턴의 수월한 구문이기 때문에, 다음은 서로 같은 겁니다:

let someOptional: Int? = 42
// 열거체 case 패턴으로 맞춰봅니다.
if case .some(let x) = someOptional {
  print(x)
}

// 옵셔널 패턴으로 맞춰봅니다.
if case let x? = someOptional {
  print(x)
}

옵셔널 패턴은 편리한 방법을 제공하여 for-in 문의 옵셔널 값 배열을 반복하는데, nil-아닌 원소에만 반복문 본문을 실행합니다:

let arrayOfOptionalInts: [Int?] = [nil, 2, 3, nil, 5]
// nil-아닌 값만 맞춰봅니다.
for case let number? in arrayOfOptionalInts {
  print("Found a \(number)")
}
// Found a 2
// Found a 3
// Found a 5

GRAMMAR OF AN ENUMERATION CASE PATTERN 부분 생략 - 링크

Type-Casting Patterns (타입-변환 패턴)

두 개의 타입 변환 패턴이 있는데, is 패턴과 as 패턴입니다. is 패턴은 switch 문의 case 이름표에만 나타납니다. isas 패턴의 형식은 다음과 같습니다:

    is type-타입
    pattern-패턴 as type-타입

is 패턴은 실행 시간에 그 값의 타입이 is 패턴 오른쪽에 지정한 타입-또는 그 타입의 하위 클래스-와 똑같으면 맞춰봅니다. is 패턴은 둘 다 타입 변환을 한다는 건 is 연산자 같이 동작하지만 반환 타입을 버립니다.

as 패턴은 실행 시간에 그 값의 타입이 as 패턴 오른쪽에 지정한 타입-또는 그 타입의 하위 클래스-와 똑같으면 맞춰봅니다. 맞춤이 성공하면, 맞은 값의 타입을 as 패턴 오른쪽에서 지정한 패턴 (pattern) 으로 변환합니다.

switch 문을 써서 isas 패턴으로 값을 맞추는 예제는, Type Casting for Any and AnyObject (Any 와 AnyObject 의 타입 변환) 부분을 보기 바랍니다.

GRAMMAR OF A TYPE CASTING PATTERN 부분 생략 - 링크

Expression Pattern (표현식 패턴)

표현식 패턴 (expression pattern) 은 표현식의 값을 나타냅니다. 표현식 패턴은 switch 문의 case 이름표 안에서만 나타납니다.

표현식 패턴이 나타내는 표현식과 입력 표현식 값의 비교는 스위프트 표준 라이브러리의 ~= 연산자4 로 합니다. ~= 연산자가 true 를 반환하면 맞춤이 성공합니다. 기본적으로, ~= 연산자는 == 연산자로 동일한 타입의 두 값을 비교합니다. 아래 예제에서 보는 것처럼, 범위 안에 값이 담겼는지를 검사하여, 값을 값의 범위와도 맞춰볼 수 있습니다.

let point = (1, 2)
switch point {
case (0, 0):
  print("(0, 0) is at the origin.")
case (-2...2, -2...2):
  print("(\(point.0), \(point.1)) is near the origin.")
default:
  print("The point is at (\(point.0), \(point.1)).")
}
// "(1, 2) is near the origin." 를 인쇄함

~= 연산자를 중복 정의하여 자신만의 표현식 맞춤 동작을 제공할 수도 있습니다. 예를 들어, 위의 예제를 재작성하여 point 표현식과 점의 문자열 표현법을 비교할 수 있습니다.

// ~= 연산자 중복 정의하여 문자열과 정수를 맞춰봅니다.
func ~= (pattern: String, value: Int) -> Bool {
  return pattern == "\(value)"
}
switch point {
case ("0", "0"):
  print("(0, 0) is at the origin.")
default:
  print("The point is at (\(point.0), \(point.1)).")
}
// "The point is at (1, 2)." 를 인쇄함

GRAMMAR OF AN EXPRESSION PATTERN 부분 생략 - 링크

다음 장

Generic Parameters and Arguments (일반화 매개 변수와 인자) >

참고 자료

  1. 원문은 Patterns 에서 확인할 수 있습니다. 

  2. let 을 각 식별자 패턴으로 배포한다’ 는 건, 본문 뒤의 설명 처럼, let (x, y) 를 자동으로 (let x, let y) 로 만들어 준다는 의미입니다. 

  3. 스위프트 프로그래밍에서 유효하다 (valid) 또는 무효하다 (invalid) 라는 말이 나오는데, 기준은 컴파일이 되느냐 안되느냐의 차이입니다. 즉, 본문 예제가 유효하지 않는다는 건 컴파일이 되지 않는 코드라는 의미입니다. 

  4. Zafar IvaevWhat is the ~= Operator in Swift? 항목을 참고하면, 스위프트 표준 라이브러리의 ~= 연산자는 범위 (range) 안의 각 값을 == 연산자로 비교하는 것이라고 합니다. 즉, 특정한 값이 그 범위 안에 있는지 검사하는, 일종의 ‘범위 비교 연산자’ 라고 할 수 있습니다.