공식 문서로 공부하는 Swift (11) - 서브스크립트

ci·2020년 5월 29일
1

Subscript

클래스, 구조체, 열거형은 컬렉션, 리스트, 시퀀스의 멤버 원소에 쉽게 접근할 수 있는 서브스크립트(subscript)를 정의할 수 있다. 다른 메소드를 사용할 필요 없이, 인덱스를 통해 값을 설정하거나 찾을 수 있다. 예를 들어 배열 인스턴스의 원소에 someArray[index]와 같이 접근할 수 있다. 딕셔너리의 원소에는 someDictionary[key]와 같이 접근한다.

같은 타입에 대해 여러 서브스크립트를 정의할 수 있고, 서브스크립트에 넣는 인덱스 값의 타입에 따라 적당한 서브스크립트를 오버로드 할 수도 있다. 서브스크립트는 일차원 공간에만 한정되지 않는다. 커스텀 타입의 필요에 맞춰 여러 매개 변수를 넣을 수도 있다.



서브스크립트 문법

서브스크립트는 인스턴스 이름 뒤 대괄호 안에 하나 이상의 값을 작성함으로써 타입의 인스턴스에 질의할 수 있도록 해 준다. 서브스크립트 문법은 인스턴스 메소드 문법, 계산 프로퍼티 문법과 유사하다. subscript 키워드와 함께 정의하고, 인스턴스 메소드와 같이 하나 이상의 매개 변수와 반환 값을 지정한다. 인스턴스 메소드와 달리 서브스크립트는 읽기 전용으로 할 수도 있고, 읽기와 쓰기가 모두 되도록 할 수도 있다. 계산 프로퍼티에서 사용한 방법과 유사하게 게터와 세터에 의해 작동한다.

subscript(index: Int) -> Int {
    get {
        // Return an appropriate subscript value here.
    }
    set(newValue) {
        // Perform a suitable setting action here.
    }
}

newValue의 타입은 서브스크립트의 반환 값과 같다. 계산 프로퍼티처럼, 세터에 newValue 파라미터를 특정하지 않으면 기본 매개 변수인 newValue가 세터에 제공된다.


읽기 전용 계산 프로퍼티처럼, 읽기 전용 서브스크립트는 get 키워드를 제거함으로써 간단하게 선언할 수 있다.

subscript(index: Int) -> Int {
    // Return an appropriate subscript value here.
}

아래 예시는 읽기 전용 서브스크립트를 구현한다. 정수의 n 곱셈 테이블을 표현하는 TimesTable 구조체를 정의한다.

struct TimesTable {
    let multiplier: Int
    subscript(index: Int) -> Int {
        return multiplier * index
    }
}
let threeTimesTable = TimesTable(multiplier: 3)
print("six times three is \(threeTimesTable[6])")
// Prints "six times three is 18"

서브스크립트를 호출하여 threeTimesTable 인스턴스에 질의를 할 수 있다.



서브스크립트 사용 (Subscript Usage)

"서브스크립트"의 정확한 의미는 그것이 사용되는 문맥에 달려 있다. 서브스크립트는 일반적으로 컬렉션, 리스트, 시퀀스의 멤버 원소에 대한 접근을 축약한 것이다. 특정한 클래스 또는 구조체의 기능을 위해 다양한 방법으로 서브스크립트를 구현할 수도 있다.

예를 들어, Swift의 딕셔너리 타입은 딕셔너리 인스턴스에 값을 저장하고, 검색하기 위해 서브스크립트를 구현한다. 서브스크립트 괄호 안에 키를 제공함으로써 딕셔너리에 값을 설정할 수 있다. 또한 서브스크립트로 딕셔너리에 값을 할당할 수도 있다.

var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
numberOfLegs["bird"] = 2

Swift의 딕셔너리 타입은 옵셔널 타입을 받고 반환하는 key-value의 서브스크립트를 구현한다. 모든 키가 값을 가진 것은 아니며, 키에 nil을 할당함으로써 값을 삭제하는 방법을 제공하기 위해 옵셔널 서브스크립트 타입을 사용한다.



서브스크립트 옵션 (Subscript Options)

서브스크립트는 여러 입력 매개 변수를 받을 수 있으며, 이 입력 매개 변수는 어떤 타입이든 될 수 있다. 서브스크립트는 어떤 타입의 값이든 반환할 수 있다.

함수처럼 서브스크립트는 다른 개수의 매개 변수를 받을 수도 있고, 기본 값을 설정할 수도 있다. 하지만 인아웃 매개 변수는 사용할 수 없다.

클래스와 구조체는 필요한 만큼 많은 서브스크립트를 제공할 수 있다. 값(들)의 타입을 기반으로 적당한 서브스크립트가 추론된다. 이러한 다중 서브스크립트 정의를 서브스크립트 오버로딩(subscript overloading)이라고 한다.

한 개의 매개 변수를 취하는 서브스크립트가 보편적이지만, 다수의 매개 변수를 서브스크립트에 정의할 수 있다. 아래 예시는 Double 값의 2차원 행렬을 표현하는 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 두 매개 변수를 취하는 initializer를 제공한다. 그리고 Double 타입의 값을 저장하는 rows * columns 크기의 배열을 생성한다. 행렬 각 위치의 초기 값은 0.0이다.


적당한 행과 열 개수를 넣어서 새로운Matrix 인스턴스를 만들 수 있다.

var matrix = Matrix(rows: 2, columns: 2)
subscriptMatrix01_2x

행열의 값은 쉼표로 구분된 서브스크립트에 행, 열 값을 넣어서 만들 수 있다.

matrix[0, 1] = 1.5
matrix[1, 0] = 3.2
subscriptMatrix02_2x

Matrix 서브스크립트의 게터와 세터는 모두 rowcolumn이 유효한지 확인하기 위한 assertion을 포함하고 있다. 이러한 assertion을 보조하기 위해 MatrixindexIsValid(row:column:)를 포함한다.

func indexIsValid(row: Int, column: Int) -> Bool {
    return row >= 0 && row < rows && column >= 0 && column < columns
}

let someValue = matrix[2, 2]
// This triggers an assert, because [2, 2] is outside of the matrix bounds.


타입 서브스크립트 (Type Subscripts)

인스턴스 서브스크립트는 특정 타입의 인스턴스에서 호출하는 서브스크립느다. 타입 자체에서 호출하는 서브스크립트 역시 정의할 수 있는데, 이를 타입 서브스크립트(type subscript)라고 부른다. static 키워드를 subscript 키워드 앞에 작성하여 타입 서브스크립트를 지시할 수 있다. 클래스는 자식 클래스가 부모 클래스의 서브스크립트 구현을 오버라이딩 하는 것을 허용하기 위해 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개의 댓글