본문 바로가기

Swift/Swift Language

Swift 언어 가이드 - 서브스크립트 Subscript

서브스크립트 Subscripts

클래스, 스트럭쳐, 이늄은 서브스크립트를 정의할 수 있습니다. 이것은 컬렉션의 멤버 원소에 접근할 때 사용하는 단축어입니다. 여러분은 서브스크립트를 이용하여 별도의 메서드 없이 인덱스 기반으로 값에 접근하거나 값을 저장할 수 있습니다. 예를 들어, Array 인스턴스의 원소에 접근하려면 someArray[index] 로 작성하거나 Dictionary 인스턴스의 원소에 접근하려면 someDictionary[key] 를 사용할 수 있습니다.

단일한 타입의 여러 서브스크립트를 정의하려면, 오버로드된 적절한 서브스크립트가 선택됩니다. 서브스크립트는 단일한 차원에만 국한되지 않으며 여러 입력 파라미터를 이용하여 커스텀 타입의 요구에 맞게 서브스크립트를 정의할 수 있습니다.

서브스크립트 문법

서브스크립트는 인스턴스를 질의(query)할 수 있도록 해줍니다. 이 때, 인스턴스 이름 뒤에 하나 이상의 값을 대괄호에 작성합니다. 서브스크립트의 문법은 인스턴스 메서드 문법이나 계산 프로퍼티 문법과 비슷합니다. 서브스크립트를 정의하려면 subscript 키워드를 이용합니다. 인스턴스 메서드의 문법처럼 하나 이상의 입력 파라미터와 리턴 타입을 정의할 수 있습니다. 인스턴스 메서드와 드렉, 서브스크립트는 읽기와 쓰기가 가능하며 혹은 읽기만 가능합니다. 이러한 동작은 계산 프로퍼티의 수정자, 접근자 메서드와 소통하는 것과 비슷합니다.

subscript(index: Int) -> Int {
    get {
    // 적절한 서브스크립트 값을 반환합니다.
  }
  set(newValue) {
      // 적합한 설정을 수행합니다.
  }
}

newValue 의 타입은 서브스크립트의 반환 타입과 값습니다. 계산 프로퍼티처럼, 수정자 메서드(setter)의 파라미터를 정의하지 않을 수 있습니다. 기본 파라미터 이름은 newValue 가 적용됩니다.

읽기 전용 계산 프로퍼티와 같이, 읽기 전용 서브스크립트를 사용할 수 있습니다. 이 때, get 키워드와 중괄호를 생략합니다.

subscript(index: Int) -> Int {
    // 적절한 서브스크립트 값을 반환합니다. 
}

여기에 읽기 전용의 서브크립트 구현이 있습니다. 이것은 TimesTable 스트럭쳐를 구현하여 n번째 구구단의 정수를 표현합니다.

struct TimesTable {
    let multiplier: Int
  subscript(index: Int) -> Int {
      return multiplier * index 
  }
}
let threeTimesTable = TimesTable(multiplier: 3)
print("6곱하기 3은 \(threeTimesTable[6])")
// "6곱하기 3은 18")이 출력됨

이 예제에서, TimesTable 의 새로운 인스턴스는 구구단의 3단을 의미합니다. 스트럭쳐의 initializer 가 3을 multiplier 프로퍼티로 저장합니다.

이후에 threeTimesTable 인스턴스를 쿼리하기 위하여 서브스크립트를 호출하였습니다. threeTimesTable[6] 를 이용하여 18의 값을 반환하였습니다.

참고

n-곱하기-표는 고정된 수학적 규칙에 기반합니다. 따라서 threeTimesTable[someIndex] 를 새로운 값으로 저장하는 것은 적절하지 않습니다. 따라서 TimesTable 의 서브크립트가 읽기전용 서브크립트로 정의된 것입니다.

서브스크립트 사용법

"서브스크립트(subscript)" 의 정확한 의미는 이것이 사용되는 문맥에 따라 달라집니다. 서브스크립트는 일반적으로 컬렉션, 리스트 또는 시퀀스의 멤버 원소에 접근할 때 사용되는 단축어입니다. 여러분은 서브스크립트를 마음대로 구현하여 특정 클래스나 스트럭쳐의 기능에 맞출 수 있습니다.

예를 들어, 스위프트의 Dictionary 타입은 이 타입의 인스턴스에 저장된 값들을 가져오고 수정하기 위해 서브스크립트를 구현합니다. 여러분이 딕셔너리의 값을 설정할 때, 이 딕셔너리의 키 타입에 맞는 키를 전달하고 딕셔너리의 값을 할당합니다.

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

위 예제는 numberOfLegs 라는 변수를 정의하고 이것을 딕셔너리 리터럴을 이용하여 초기화하였습니다. numberOfLegs 딕셔너리의 타입은 [String: Int] 로 추론됩니다. 이 딕셔너리가 생성되고 나면, 키 값이 "bird"의 Int 값을 2로 저장합니다.

딕셔너리의 서브스크립트에 대한 자세한 내용은 Accessing and Modifying a Dictionary 을 참고하세요.

참고

스위프트의 Dictionary 타입은 키-밸류 서브스크립트를 구현합니다.이 것은 서브스크립트를 받아서 옵셔널 타입을 반환합니다. 위 예제의 numberOfLegs 딕셔너리를 살펴보면, 키-밸류 서브스크립트는 Int? 값 즉, 옵셔널 정수 값을 반환합니다. Dictionary 타입은 옵셔널 서브스크립트 타입을 사용하여 모든 키가 값을 가지지 않을 수도 있다는 것을 가정합니다. 키 값에 대해 nil 밸류를 할당하면 해당 키의 값을 삭제할 수 있습니다.

서브스크립트 옵션

서브스크립트는 어떠한 갯수의 입력 파라미터도 받을 수 있습니다. 또한, 입력 파라미터는 어떠한 타입이든 받을 수 있습니다. 서브스크립트는 어떠한 타입의 값이든 반환할 수 있습니다.

함수처럼, 서브스크립트는 임의의 갯수의 파라미터를 받으며 이러한 파라미터의 값의 기본 값을 제공할 수 있습니다. 하지만, 함수와 다른점은 서브스크립트의 인-아웃 파라미터는 존재하지 않습니다.

클래스나 스트럭쳐는 서브스크립트 구현을 직접 할 수 있습니다. 적절한 서브스크립트가 서브스크립트가 사용되는 시점에서 브라켓 안에 있는 값의 타입에 따라 추론됩니다. 이러한 여러 개의 서브스크립트를 정의하는 것을 서브스크립트 오버로딩 이라고 합니다.

서브스크립트가 하나의 파라미터를 받는 것이 일반적이지만, 서브스크립트가 여러 파라미터를 받을 수 있도록 정의할 수 있습니다. 아래의 예제는 Matrix 스트럭쳐를 정의한 예제입니다. 이 스트럭쳐는 2 차원의 Double 의 행렬을 표현합니다. 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 && colum >= 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
    }
  }
}

Matrix 스트럭쳐는 두 개의 파라미터인 rowscolumns 를 받는 생성자를 가집니다. 또한, 타입이 Doublerows * columns 를 저장하기에 충분한 배열을 생성합니다. 행렬의 각 위치는 초기에 0.0 으로 저장되야 합니다. 따라서, 배열의 생성자가 0.0 의 값을 초기 값으로 전달 받으며 적절한 크기를 이용해 새로운 배열을 생성합니다. 이 생성자에 대한 자세한 사항은 Creating an Array with a Default Value 을 참고하세요.

이제 새로운 Matrix 인스턴스를 행과 열의 개수를 생성자에 파라미터로 전달하여 생성합니다.

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

이 예제는 두 개의 행과 두 개의 열을 가진 Matrix 인스턴스를 생성합니다. 이 인스턴스의 grid 배열은 배열을 평평하게 늘어 놓습니다. 이 배열은 왼쪽 끝에서 오른쪽 아래로 읽을 수 있습니다.

../_images/subscriptMatrix01_2x.png

이 배열의 값을 수정하기 위해서 서브스크립트의 행과 열의 값을 전달합니다.

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

이 두개의 문장은 서브스크립트의 수정자 메서드를 호출하여 배열의 상단 오른쪽 값을 1.5로 수정합니다. ( 행이 0이고 열이 1인 위치입니다.), 그리고 하단 오른쪽의 값을 3.2로 수정합니다. (행이 1이고 열이 0인 위치입니다.)

../_images/subscriptMatrix02_2x.png

Matrix 서브스크립트의 수정자 메서드와 접근 메서드는 서브스크립트의 행과 열의 값이 유효한지를 체크하기 위해 단언(assertion)을 사용합니다. 이 단언을 도와주는 메서드인 indexIsValid(row:colum:) 는 전달 받은 파라미터인 rowcolumn 이 현재 매트릭스의 범위 안에 있는지를 확인합니다.

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) 라고 합니다. 타입 서브스크립트를 지정하려면, 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)

원문 : https://docs.swift.org/swift-book/LanguageGuide/Subscripts.html