Swift 뿌수기 - Subscripts

Wonbi·2022년 9월 23일
1

Swift 뿌수기

목록 보기
4/12

✅ 학습 내용

📌 Subscripts

Classes, structures, and enumerations can define subscripts, which are shortcuts for accessing the member elements of a collection, list, or sequence. You use subscripts to set and retrieve values by index without needing separate methods for setting and retrieval. For example, you access elements in an Array instance as someArray[index] and elements in a Dictionary instance as someDictionary[key].

  • 클래스, 구조체, 및 열거형은, 집합체 (collection), 리스트 (list), 또는 시퀀스 (sequence) 멤버 원소에 접근하는 줄임말인, 첨자 (subscripts) 를 정의할 수 있습니다. 별도의 설정 및 검색 방법 없이 첨자를 사용하여 색인 (index) 별로 값을 설정하고 검색할 수 있습니다. 예를 들어, Array 인스턴스 원소는 someArray[index]Dictionary 인스턴스 원소는 someDictionary[key] 로 접근합니다.
  • 단일 타입에 여러개의 첨자를 정의할 수 있으며, 첨자에 전달한 색인 값 타입을 기초로 중복 정의한 첨자 중 사용하기 적절한 것을 선택한다. 첨자는 1차원으로 제안하지 않으며, 자신이 정의한 타입에 적합하도록 여러 개의 입력 매개변수를 가진 첨자를 정의할 수도 있다.

💎 첨자 구문

  • 첨자는 인스턴스 이름 뒤 대괄호 안에 하나 이상의 값을 작성함으로써 타입의 인스턴스를 조회할 수 있게 한다. 이러한 구문은 인스턴스 메서드 구문 및 연산 프로퍼티 구문과 비슷하다. 첨자 정의는 subscript키워드로 작성하며, 인스턴스 메서드와 똑같이 하나 이상의 입력 매개변수와 반환 타입을 지정한다. 인스턴스 메서드와 달리, 첨자는 읽기-쓰기나 읽기 전용일 수 있다. 이런 동작은 연산 프로퍼티에서와 똑같이 gettersetter로 전달된다.
subscript(index: Int) -> Int {  
  get {
    // 여기에 적절한 첨자 값을 반환합니다.
  }
  set(newValue) {
    // 여기서 적절한 설정 작업을 수행합니다.
  }
}
  • newValue타입은 첨자의 반환 값과 똑같다. 연산 프로퍼티처럼, setter(newValue) 매개변수를 지정하지 않을 수 있다. 만약 매개변수를 지정하지 않으면 newValue 라는 기본 매개 변수를 setter에 제공한다.

  • get-only 연산 프로퍼티처럼, get 키워드와 괄호를 제거하여 get-only 첨자 정의를 단순하게 할 수 있다.

subscript(index: Int) -> Int {
  // 여기에 적절한 첨자 값을 반환합니다.
}
  • 다음 예제는, 정수 구구단 n-단을 나타내는 TimesTable 구조체를 정의한 get-only 첨자 구현이다.
struct TimesTable {
  let multiplier: Int
  subscript(index: Int) -> Int {
    return multiplier * index
  }
}
let threeTimesTable = TimesTable(multiplier: 3)
print("3 곱하기 6은 \(threeTimesTable[6])")
// "3 곱하기 6은 18" 을 인쇄함
  • 이 예제에선 3단을 나타내는 TimesTable 인스턴스를 생성한다. 이는 인스턴스의 multiplier 프로퍼티가 사용할 3이라는 값을 구조체의 이니셜라이저에 전달함으로써 지시한다.

  • threeTimesTable[6]이라는 호출로 보는 것처럼, 자신의 첨자를 호출함으로써 threeTimesTable 인스턴스를 조회할 수 있다. 이는 3단의 6번째 요소를 요청하여 18, 혹은 3 곱하기 6 의 값을 반환한다.

구구단 n-단은 고정된 수학 규칙에 기초합니다. threeTimesTable[someIndex] 에 새 값을 설정하는 건 적절치 않으므로, TimesTable 의 첨자는 읽기-전용 첨자로 정의합니다.

💎 첨자 사용

  • 첨자 (subscript) 의 정확한 의미는 자신을 사용한 곳의 상황에 따라 다르다. 첨자는 일반적으로 컬렉션, 리스트, 또는 시퀀스의 구성원 요소에 접근하기 위한 줄임말로 사용한다. 특정 클래스나 구조체 기능에 가장 적절한 방식으로 자유롭게 첨자를 구현할 수 있다.

  • 예를 들어, 스위프트의 Dictionary 타입은 Dictionary 인스턴스에 저장한 값을 설정하고 가져오고자 첨자를 구현한다. 첨자 대괄호 안에 딕셔너리의 키 타입인 Key를 제공하고, 첨자에 딕셔너리의 값 타입인 Value를 할당함으로써, 딕셔너리에 값을 설정할 수 있다.

var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
numberOfLegs["bird"] = 2
  • 위 예제는 numberOfLegs 라는 변수를 정의하고 3개의 키-값 쌍을 담은 딕셔너리로 이를 초기화한다. numberOfLegs 딕셔너리의 타입은 [String: Int] 라고 추론한다. 딕셔너리를 생성한 후, 이 예제는 첨자 할당을 사용하여 딕셔너리에 "bird" 라는 String 키와 2 라는 Int 값을 추가한다.

스위프트의 Dictionary 타입은 옵셔널 (optional) 타입을 취하고 반환하는 첨자로 자신의 키-값 첨자 연산을 구현합니다. 위의 numberOfLegs 딕셔너리에선, 키-값 첨자가 Int?, 또는 “옵셔널 정수 (optional int)”, 타입의 값을 취하고 반환합니다. Dictionary 타입은 옵셔널 첨자 타입을 사용하여 모든 키가 값을 가지진 않을 거라는 사실을 모델링하고, 그 키에 nil 값을 할당함으로써 키의 값을 삭제할 방법을 제공합니다.

💎 첨자 옵션

  • 첨자는 임의의 개수의 입력 매개변수를 사용할 수 있으며, 이러한 입력 매개변수는 임의의 타입일 수 있다. 첨자는 모든 타입의 값을 반환할 수도 있다.

  • 마치 함수처럼 첨자는 다양한 수의 매개변수를 사용할 수 있으며, 매개 변수에 대한 기본값을 제공할 수 있다. 그러나 in-out 파라미터는 사용할 수 없다.

  • 클래스 혹은 구조체는 필요한 만큼의 첨자 구현을 제공할 수 있으며, 사용할 적절한 첨자는 첨자가 사용되는 시점에 첨자 대괄호 안에 포함된 값의 타입을 기반으로 추론된다. 이런 여러 개의 첨자 정의를 첨자 중복 정의 (subscript overloading) 라고 한다.

  • 첨자가 단일 매개 변수를 사용하는 것이 가장 일반적이지만, 자신의 타입에 적절하다면 여러 개의 매개변수를 가진 첨자를 정의할 수도 있다. 다음 예제는, 2차원 Double 값 배열을 나타내는 Matrix 구조체를 정의한다. Matrix 구조체의 첨자는 두 정수 매개변수를 사용한다.

struct Matrix {
    let rows: Int, columns: Int
    var grid: [Double]
    
    init(rows: Int, columns: Int) {
        self.rows = rows
        self.columns = columns
        grid = Array(repeating: 0.0, count: rows * columns)
    }
    
    func indexIsValid(row: Int, column: Int) -> Bool {
        return row >= 0 && row < rows && column >= 0 && column < columns
    }
    
    subscript(row: Int, column: Int) -> Double {
        get {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            return grid[(row * columns) + column]
        }
        set {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            grid[(row * columns) + column] = newValue
        }
    }
}
  • Matrixrowscolumns 이라는 두 매개변수를 취하는 이니셜라이저를 제공하며, rows * columns 개의 Double 타입 값을 저장할만큼 충분히 큰 배열을 생성한다. 행렬 (matrix) 안의 각 위치엔 0.0 이라는 초기 값을 준다.

  • 자신의 이니셜라이저에 적절한 행과 열 개수를 전달함으로써 새로운 Matrix 인스턴스를 생성할 수 있다.

var matrix = Matrix(rows: 2, columns: 2)
  • 위 예제는 두 행과 두 열을 가진 새로운 Matrix 인스턴스를 생성합니다. 이 Matrix 인스턴스의 grid 배열은 사실상 맨 왼쪽 위에서 오른쪽 아래로 읽어가는, 납작한 버전의 행렬이다.

'납작한 (flattened) 버전의 행렬' 이란 2차원 배열의 형상을 바꿔서 1차원 배열로 만들었다는 의미입니다.

  • 행과 열 값을 쉼표로 구분하여 첨자에 전달함으로써 행렬에 있는 값을 설정할 수 있다.
matrix[0, 1] = 1.5
matrix[1, 0] = 3.2
  • 이 두 구문은 첨자의 setter를 호출하여 (row 가 0 이고 column 이 1 인) 행렬 맨 오른쪽 위를 1.5 로, (row 가 1 이고 column 이 0 인) 맨 왼쪽 아래를 3.2 라는 값으로 설정한다.

  • Matrix 첨자의 gettersetter 둘 다 첨자의 rowcolumn 값이 유효한지 검사하는 단언문 (assertion) 을 담고 있다. 이 단언문을 지원하기 위해, 요청한 rowcolumn 이 행렬 경계 안에 있는지 검사하는 indexIsValid(row:column:) 라는 편의를 위한 메소드를 가지고 있다.
func indexIsValid(row: Int, column: Int) -> Bool {
    return row >= 0 && row < rows && column >= 0 && column < columns
}
  • 행렬 범위를 벗어난 첨자에 접근하려고 하면 단언문이 트리거된다.
let someValue = matrix[2, 2]
// [2, 2] 가 행렬 경계 밖이기 때문에, 단언문을 발동합니다.

💎 타입 첨자

  • 위에서 설명한 것처럼, 인스턴스 첨자는 특정한 타입의 인스턴스에서 호출하는 첨자이다. 타입 그 자체에서 호출하는 첨자도 정의할 수 있다. 이러한 종류의 첨자를 타입 첨자 (type subscript) 라고 한다. subscript 키워드 앞에 static 키워드를 작성함으로써 타입 첨자를 표시한다. 클래스는 class 키워드를 대신 사용하여, 그 첨자의 상위 클래스 구현을 하위 클래스가 재정의하도록 허용할 수 있다. 아래 예제는 타입 첨자의 정의와 호출 방법을 보여준다.
enum Planet: Int {
    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
    
    static subscript(n: Int) -> Planet {
        return Planet(rawValue: n)!
    }
}
let mars = Planet[4]
print(mars)

0개의 댓글