Burt.K

Awesome Discovery

Swift 알고리즘 클럽: Array2D

Posted at — Aug 18, 2021

앞으로 시간이 생길 때마다 Swift 알고리즘 클럽 내용을 공부해 보자😋 우선 쉬운 자료구조부터 살펴보자. Array2D!

우선 실습을 위해서 테스트 환경을 구성하자~

$ mkdir array2d
$ cd array2d
$ swift package init

C와 Objective-C 에서 2차원 배열 선언은 아래와 같다.

int cookies[9][7];

원소 접근은 인덱스를 사용한다.

int myCookie = cookies[3][6]

Swift는 위처럼 선언할 수 없다. Swift의 2차원 배열 선언 및 초기화는 좀 장황하다.

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]
XCTAssertEqual(myCookie, 0)

물론 한 줄로 2차원 배열을 선언할 수 있다.

let cookies = [[Int]](repeating: [Int](repeating: 0, count: 7), count: 9)
let myCookie = cookies[3][6]
XCTAssertEqual(myCookie, 0)

그래도 C언어만큼 쉬워 보이진 않는다.

축약할 수 있는 함수를 정의해 2차원 배열 선언을 좀 더 쉽게 할 수 있다.

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

let cookies = dim(9, dim(7, 0))
let myCookie = cookies[3][6]
XCTAssertEqual(myCookie, 0)

dim 함수를 사용하면 2차원 보다 높은 고차원 배열을 쉽게 만들 수 있다.

let threeDimensions = dim(2, dim(3, dim(4, 0)))
let element = threeDimensions[1][1][1]
XCTAssertEqual(element, 0)

dim 함수 대신에 좀 더 명확한 Array2D 데이터 타입을 만들어 보자.

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(0 <= column && column < columns, "Column \(column) Index is out of range. Array<T>(columns: \(columns), rows:\(rows))")
            precondition(0 <= row && row < rows, "Row \(row) Index is out of range. Array<T>(columns: \(columns), rows:\(rows))")
            return array[row * columns + column]
        }
        set {
            precondition(0 <= column && column < columns, "Column \(column) Index is out of range. Array<T>(columns: \(columns), rows:\(rows))")
            precondition(0 <= row && row < rows, "Row \(row) Index is out of range. Array<T>(columns: \(columns), rows:\(rows))")
            array[row * columns + column] = newValue
        }
    }
}

1차원 배열을 사용하여 2차원 배열을 간단하게 정의해 보았다. 그리고 subscript 메서드를 구현했기 때문에 인덱스로 원소에 접근 가능하다.

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

var myCookie = cookies[3, 6]
XCTAssertEqual(myCookie, 0)

cookies[3, 6] = 10
myCookie = cookies[3, 6]
XCTAssertEqual(myCookie, 10)

위 코드에서 preconditionassert와 비슷한 구문으로 조건이 false가 되면 프로그램은 실행을 멈추고 뒤의 메시지를 출력한다.