[Swift] Subscription

Subscription Syntax

Subscription은 흔히 배열이나 사전에서 인덱스, 키를 사용하여 원소를 뽑아내는 액세스 방법을 말하는데, 일반 타입에서도 subscript 키워드를 사용하여 마치 프로퍼티처럼 사용할 수 있다.

struct TimeTable {
    let multiplier:Int
    subscript (index: Int) -> Int {
        return multiplier * index   
    }
}
let threeTimeTable = TimeTable(multiplier: 3)
println("six times three is \(threeTimeTable[6])")

서브스크립션은 함수와 비슷하게 동작하기 때문에 2개 이상의 인덱스를 받을 수 있다.

다음은 내부에 하나의 배열을 프로퍼티로 갖고, 이를 서브스크립션을 사용해 행렬처럼 다루는 구조체의 예이다.


struct Matrix { let rows: Int, columns: Int var grid: Double[] init(rows: Int, columns: Int) { self.rows = rows self.columns = columns grid = Array(count: rows * columns, repeatedValue: 0.0) } // func indexIsValidRow(row: Int, column: Int) -> Bool { return row >= 0 && row < rows && column >= 0 && columns } // subscript(row: Int, column: Int) -> Double { get { assert(indexIsValidRow(row, column: column), "index out of range") return grid[(row * column) + column] } set { assert(indexIsValidRow(row, column: column), "index out of range") return grid[(row * column) + column0] = newValue } } } var matrix = Matrix(rows:2, columns:2) matrix[0, 1] = 1.5 matrix[1, 0] = 3.2

서브스크립트는 단일 객체 내에서 여러 종류를 만들 수 있다. 다음 예를 보자.


struct TimeTable { let time:Int var times:Array<String> { return ["one":1, "two":2, "three":3, "four":4] } subscript(index:Int) -> Int { return time * index } subscript(index:String) -> Int? { if contains(times.keys, index) { return time * times[index]! } return nil } }

이 예는 글의 서두에 등장한 예제를 확장한 것이다. 입력을 정수가 아닌 문자열로 받아서, 미리 정의된 사전에 대응되는 값으로 변환해주는 것이다. 즉 서브스크립트는 오버로딩가능하다는 이야기이다.

이 코드에 대한 간략한 설명을 첨부한다.

  1. times는 문자열을 정수값을 변환하기 위한 해시테이블이다.
  2. computed property에 대해 구조체에서는 lazy 변경자를 쓸 수 없다.
  3. 문자열을 인덱스로 받는 경우, 사전에 없는 값이 들어오면 nil을 리턴하게 끔 되어 있으므로, 이 리턴타입은 Int? 형이다.
  4. 놀랍게도 사전의 서브스크립션 리턴타입은 옵셔널이다. 사전은 존재하지 않는 키에 대해 쿼리하면 예외를 내는 대신, nil을 리턴한다.