[Swift] Subscripts

상 원·2022년 7월 16일
0

Swift

목록 보기
20/31
post-thumbnail

콜렉션, 리스트, 시퀀스의 멤버에 접근하기 위한 shortcut

Subscript는 구조체, 클래스, 열거형에 정의할 수 있는데,
별다른 메소드를 정의하지 않고도 인덱스를 이용해서 값을 설정하고 반환해낼 수 있다는 장점이 있다.
그니까 Array[index], Dictionary[key] 처럼 값의 일부분을 가져올 수 있다는 것!
음.. 그니까 [ ] 요 안에 숫자를 넣어서 인덱스로 어떤 타입을 접근할 때 쓰는 것이 subscript라고 생각하면 될듯

Subscript Syntax

배열의 subscript는 기본적으로 다음과 같이 정의되어 있음.

@inlineable public subscript(index: Int) -> Element

얘는 인덱스로 정수를 받고, 해당 인덱스의 Element를 반환하는 형식!

딕셔너리의 subscript의 경우에는,

@inlineable public subscript(key: Key) -> Value?

요런식으로 정의돼 있음.
키값을 인덱스로 받아서 그것에 대한 value를 반환해 주는 형식!

요 subscript를 내 입맛대로 작성해서 타입마다 붙여줄 수가 있다.

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

계산 프로퍼티랑 되게 비슷하게 생겼쥬?
getter, setter가 동일하게 있고, setter는 있어도 되고 없어도 됨!


구조체나 클래스, 열거형에 적용할 수가 있는데, 일단 구조체에다가 한번 적용해보면

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"

TimesTable 구조체는 약간 구구단 표같은 느낌? 어떤 숫자에 인덱스별로 곱한 걸 저장해두는 테이블을 나타낸다고 보면 될듯.
threeTimesTable 은 인덱스별로 3에 대한 각 숫자의 곱셈결과를 저장해두는 거.
그래서 threeTimesTable[6] 로 subscript를 호출해주면 3*6=18 의 값이 출력됨!


만약에 문자열을 인덱스로 접근하고 싶다면?
String 타입을 확장해서 subscript를 넣어주면 된다.

extension String {
    subscript(idx: Int) -> String? {
        guard (0..<count).contains(idx) else {
            return nil
        }
        let target = index(startIndex, offsetBy: idx)
        return String(self[target])
    }
}

String을 확장해서 인덱스로 문자를 접근할 수 있는 subscript를 등록해주면 된다.
그러면~

let str = "Hello, swift!"
str[0]           // Optional("H")
str[100]         // nil

이런식으로 문자열을 인덱스로 접근할 수 있게 됨! (원래는 오류남)

Subscript Options

Subscript는 매개변수 갯수의 제한이 없다. 대신 in-out 매개변수는 못씀!
in-out이 뭐냐면, 함수글에서 나왔던 건데, 매개변수로 받은 함수 밖의 변수들의 값을 바꿔주는 매개변수 옵션임.
암튼 이걸 매개변수로 못 가지고..

클래스나 구조체는 subscript를 필요한 만큼 구현해서 쓸 수가 있다.
이걸 사용할 때는 subscript를 사용할 때 [ ] 안에 있는 값에 따라 알아서 구분돼서 사용됨.
이게 바로 subscript overloading 이라고 함!

이번 예제는 여러 개의 매개변수를 가지는 subscript이다.

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
        }
    }
}

2차원 배열을 나타내는 구조체임.

var matrix = Matrix(rows: 2, columns: 2)
matrix[0, 1] = 1.5
matrix[1, 0] = 3.2
let someValue = matrix[2, 2]
// This triggers an assert, because [2, 2] is outside of the matrix bounds.

2x2 행렬을 생성하고, [0, 1], [1, 0] 에다 값을 집어넣은 상태에서
행렬의 다른 인덱스에 값을 접근하려고 시도하는 코드임.
someValue가 [2, 2]에 접근을 시도하면, subscript를 거치는데, getter에서 assert(indexIsValid()) 가 실행된다.
여기서 인덱스의 범위가 접근할 수 있는 범위를 넘어섰으므로, assertion이 일어나는 구조임.

Type Subscripts

위에서는 인스턴스 서브스크립트로 쓴 거고, 타입 서브스크립트도 당연히 가능하다!!

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)
profile
ios developer

0개의 댓글