2차원 배열

C와 Objective-C에서는 다음과 같이 코드를 작성해 9x7 크기의 쿠키 그리드를 만들 수 있다.

int cookies[9][7];

이 코드는 63개의 요소를 가진 2차원 배열을 생성한다. 3번째 컬럼과 6번째 행에 있는 쿠키를 찾으려면 다음과 같이 작성한다.

myCookie = cookies[3][6];

하지만 이 문법은 Swift에서 사용할 수 없다. Swift에서 다차원 배열을 생성하려면 다음과 같이 작성한다.

var cookies = [[Int]]()
for _ in 1...9 {
  var row = [Int]()
  for _ in 1...7 {
    row.append(0)
  }
  cookies.append(row)
}

그런 다음, 쿠키를 찾으려면 다음과 같이 작성한다.

let myCookie = cookies[3][6]

또한, 배열을 한 줄로 생성할 수도 있다.

var cookies = [[Int]](repeating: [Int](repeating: 0, count: 7), count: 9)

이 코드는 복잡해 보이지만, 헬퍼 함수를 사용해 간단히 만들 수 있다.

func dim<T>(_ count: Int, _ value: T) -> [T] {
  return [T](repeating: value, count: count)
}

그러면 배열을 다음과 같이 생성할 수 있다.

var cookies = dim(9, dim(7, 0))

Swift는 배열 요소의 기본값으로 0을 지정했기 때문에 배열의 데이터 타입이 Int임을 추론한다. 문자열을 사용하려면 다음과 같이 작성한다.

var cookies = dim(9, dim(7, "yum"))

dim() 함수를 사용하면 더 많은 차원의 배열도 쉽게 만들 수 있다.

var threeDimensions = dim(2, dim(3, dim(4, 0)))

하지만 이렇게 다차원 배열이나 중첩된 배열을 사용하면 각 차원이 무엇을 의미하는지 헷갈릴 수 있다.

대신, 2차원 배열처럼 동작하는 커스텀 타입을 만들어 사용하면 더 편리하다.

public struct Array2D<T> {
  public let columns: Int
  public let rows: Int
  fileprivate var array: [T]
  
  public init(columns: Int, rows: Int, initialValue: T) {
    self.columns = columns
    self.rows = rows
    array = .init(repeating: initialValue, count: rows*columns)
  }
  
  public subscript(column: Int, row: Int) -> T {
    get {
      precondition(column < columns, "Column \(column) Index is out of range. Array<T>(columns: \(columns), rows:\(rows))")
      precondition(row < rows, "Row \(row) Index is out of range. Array<T>(columns: \(columns), rows:\(rows))")
      return array[row*columns + column]
    }
    set {
      precondition(column < columns, "Column \(column) Index is out of range. Array<T>(columns: \(columns), rows:\(rows))")
      precondition(row < rows, "Row \(row) Index is out of range. Array<T>(columns: \(columns), rows:\(rows))")
      array[row*columns + column] = newValue
    }
  }
}

Array2D는 제네릭 타입이므로 숫자뿐만 아니라 모든 종류의 객체를 저장할 수 있다.

Array2D의 인스턴스를 생성하려면 다음과 같이 작성한다.

var cookies = Array2D(columns: 9, rows: 7, initialValue: 0)

subscript 함수를 사용해 배열에서 객체를 가져올 수 있다.

let myCookie = cookies[column, row]

또는 값을 변경할 수도 있다.

cookies[column, row] = newCookie

내부적으로 Array2D는 데이터를 저장하기 위해 단일 1차원 배열을 사용한다. 객체의 인덱스는 (행 x 컬럼 수) + 컬럼으로 계산되지만, Array2D를 사용하는 개발자는 "컬럼"과 "행"만 생각하면 되며, 나머지 세부 사항은 Array2D가 처리한다. 이렇게 기본 타입을 래퍼 클래스나 구조체로 감싸는 것이 장점이다.

Swift Algorithm Club의 Matthijs Hollemans가 작성함