콜렉션, 리스트, 시퀀스의 멤버에 접근하기 위한 shortcut
Subscript는 구조체, 클래스, 열거형에 정의할 수 있는데,
별다른 메소드를 정의하지 않고도 인덱스를 이용해서 값을 설정하고 반환해낼 수 있다는 장점이 있다.
그니까 Array[index]
, Dictionary[key]
처럼 값의 일부분을 가져올 수 있다는 것!
음.. 그니까 [ ] 요 안에 숫자를 넣어서 인덱스로 어떤 타입을 접근할 때 쓰는 것이 subscript라고 생각하면 될듯
배열의 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는 매개변수 갯수의 제한이 없다. 대신 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이 일어나는 구조임.
위에서는 인스턴스 서브스크립트로 쓴 거고, 타입 서브스크립트도 당연히 가능하다!!
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)