Golang 다차원 배열

GolangBeginner
지금 연습하기

소개

이전 섹션에서 배열의 기본 사용법에 대해 배웠습니다.

배열에는 어떤 유형의 요소가 들어갈 수 있을까요?

Go 에서 배열의 요소는 정수, 문자열 또는 사용자 정의 유형과 같은 모든 기본 유형일 수 있습니다.

배열의 요소가 배열인 경우 어떻게 될까요?

그러면 다차원 배열이 됩니다.

Multidimensional Array

위 그림과 같이 보라색 상자가 원래 배열입니다.

보라색 상자 배열의 각 요소는 새로운 배열 (빨간색 상자) 입니다.

핵심 개념:

  • 2 차원 배열의 정의
  • 2 차원 배열의 초기화
  • 2 차원 배열 순회 (Traversing)
  • 다차원 배열의 사용

2 차원 배열 정의

일반적인 정의

일반 배열을 정의하는 방법을 기억하시나요?

var variableName [elementCount]variableType

그렇다면 이 가장 기본적인 방법으로 2 차원 배열을 어떻게 정의할까요?

var variableName [elementCount][elementCount]variableType

유일한 차이점은 원래 [elementCount] 앞에 다른 [elementCount]를 추가하여 [elementCount][elementCount] 형식을 형성한다는 것입니다.

예를 들어, int 타입의 10*10 용량을 가진 a라는 2 차원 배열을 정의하려면 다음 구문을 사용할 수 있습니다.

var a [10][10]int

간결한 정의

짧은 변수 선언과 마찬가지로, 간결한 정의 방법을 사용하여 배열, 심지어 다차원 배열도 선언할 수 있습니다.

:=를 사용하여 다차원 배열을 포함한 배열을 선언할 수 있습니다.

예를 들어:

a := [10][10]int{}

이 방법으로 크기가 10*10인 정수 2 차원 배열을 정의합니다.

2 차원 배열 초기화

1 차원 배열과 유사하게, 2 차원 배열을 다음과 같은 방법으로 초기화할 수 있습니다.

  • 초기화 목록 (Initialization list)
  • 길이 추론 초기화 (Inferred length initialization)
  • 지정된 인덱스 값 초기화 (Specified index value initialization)

이러한 방법을 어떻게 사용하는지 기억하시나요?

이전 섹션에서 1 차원 배열에서 이러한 방법을 사용하는 방법을 배웠습니다. 잊어버렸더라도 괜찮습니다.

다음 섹션에서는 이 세 가지 초기화 방법을 검토하고 이를 2 차원 배열로 확장합니다.

초기화 목록을 사용한 2 차원 배열 초기화

이전 섹션에서 생성한 array.go 파일을 계속 사용해 보겠습니다. 작업을 저장하지 않았다면 다음과 같이 파일을 생성할 수 있습니다.

touch ~/project/array.go

array.go에 다음 코드를 작성합니다.

package main

import "fmt"

func main() {
    // 자동으로 0 으로 초기화
    var simpleArray [3][3]int
    // 지정된 초기 값을 사용하여 초기화, 누락된 요소에 대한 기본값 사용
    var numArray = [3][3]int{{1, 2, 3}, {2, 3, 4}}
    // 지정된 초기 값을 사용하여 초기화
    var cityArray = [2][2]string{{"London", "Chengdu"}, {"Paris", "Boston"}}
    fmt.Println(simpleArray) // [[0 0 0] [0 0 0] [0 0 0]]
    fmt.Println(numArray)    // [[1 2 3] [2 3 4] [0 0 0]]
    fmt.Println(cityArray)   // [[London Chengdu] [Paris Boston]]
}

위 코드는 초기화 목록을 사용하여 2 차원 배열을 초기화하는 세 가지 방법을 보여줍니다.

다음 명령을 사용하여 코드를 실행합니다.

go run ~/project/array.go

출력 결과는 다음과 같습니다.

[[0 0 0] [0 0 0] [0 0 0]]
[[1 2 3] [2 3 4] [0 0 0]]
[[London Chengdu] [Paris Boston]]

값을 수정하고 1 차원 배열에 대한 초기화 방법을 검토할 수 있습니다.

추론된 길이를 사용한 2 차원 배열 초기화

2 차원 배열에서 1 차원 배열에서 했던 것처럼 길이 추론 (inferred length) 방법을 사용하여 초기화할 수 있습니다.

array.go에 다음 코드를 작성합니다.

package main

import "fmt"

func main() {
    // 자동으로 0 으로 초기화
    var simpleArray [3][3]int
    // 지정된 초기 값을 사용하여 초기화, 누락된 요소에 대한 기본값 사용
    var numArray = [...][]int{{1, 2, 3, 3}, {2, 3, 4, 3}, {0}}
    // 지정된 초기 값을 사용하여 초기화
    var cityArray = [...][2]string{{"London", "Chengdu"}, {"Paris", "Boston"}}
    fmt.Println(simpleArray) // [[0 0 0] [0 0 0] [0 0 0]]
    fmt.Println(numArray)    // [[1 2 3 3] [2 3 4 3] [0]]
    fmt.Println(cityArray)   // [[London Chengdu] [Paris Boston]]
}

위 코드는 길이 추론을 사용하여 2 차원 배열을 초기화하는 것을 보여줍니다.

go run ~/project/array.go

출력 결과는 초기화 목록 (initialization list) 방법과 동일합니다.

[[0 0 0] [0 0 0] [0 0 0]]
[[1 2 3 3] [2 3 4 3] [0]]
[[London Chengdu] [Paris Boston]]

그러나 1 차원 배열과 달리, 2 차원 배열의 길이 추론 초기화에서 ... 기호는 첫 번째 대괄호 안에만 존재할 수 있습니다.

예를 들어:

var numArray = [...][]int{{1, 2, 3, 3}, {2, 3, 4, 3}}

이 코드는 유효하지만, 다음 두 가지 변형은 잘못되었습니다.

var numArray = [][...]int{{1, 2, 3, 3}, {2, 3, 4, 3}}
var numArray = [...][...]int{{1, 2, 3, 3}, {2, 3, 4, 3}}

또한 numArraycityArray를 비교해 보겠습니다.

cityArray에서 2 차원 배열의 크기의 두 번째 매개변수를 지정하는 것을 볼 수 있습니다. 아래와 같습니다.

var cityArray = [...][2]string{{"London", "Chengdu"}, {"Paris", "Boston"}}

이는 초기화 중에 각 하위 배열의 크기2로 지정한다는 의미입니다.

초기화 중에 주어진 값이 충분하지 않으면, 데이터 유형의 기본값이 누락된 요소를 채우는 데 사용됩니다.

주어진 값의 수가 지정된 크기를 초과하면 오류가 발생합니다.

지정된 인덱스 값을 사용한 2 차원 배열 초기화

1 차원 배열과 유사하게, 2 차원 배열에서도 지정된 인덱스 값 (index value) 을 사용하여 초기화할 수 있습니다. 과정은 유사합니다.

array.go에 다음 코드를 작성합니다.

package main

import "fmt"

func main() {
    a := [...][]int{1: {1, 2, 3}, 3: {4, 7, 9}}
    fmt.Println(a)                  // [[] [1 2 3] [] [4 7 9]]
    fmt.Printf("Type of array a: %T\n", a) // Type of array a: [4][]int
}
go run ~/project/array.go

출력 결과는 다음과 같습니다.

[[] [1 2 3] [] [4 7 9]]
Type of array a: [4][]int

위 코드는 배열 a를 결정되지 않은 길이 (indeterminate length) 를 가진 2 차원 배열로 정의합니다. 인덱스 1에 값 [1 2 3]을 할당하고, 인덱스 3에 값 [4 7 9]를 할당합니다.

자동으로 추론된 길이의 경우, 배열 a의 타입은 [4][]int입니다.

2 차원 배열 순회

2 차원 배열을 어떻게 순회할까요?

1 차원 배열을 배울 때, 배열을 순회하는 두 가지 방법, 즉 range 키워드 사용과 인덱스 번호 (index number) 사용을 배웠습니다.

이제 이 두 가지 방법을 2 차원 배열을 순회하는 데 적용해 보겠습니다.

array.go에 다음 코드를 작성합니다.

package main

import "fmt"

func main() {
    a := [...][]int{{123, 321, 222}, {404, 501, 503}, {857, 419, 857}}
    // Method 1: using the range keyword
    fmt.Println("Traversing the Two-Dimensional Array Using the range Keyword")
    for index, value := range a {
        for i, j := range value {
            fmt.Println(index, i, j)
        }
    }
    // Method 2: using index numbers
    fmt.Println("\nTraversing the Two-Dimensional Array Using Index Numbers")
    for i := 0; i < len(a); i++ {
        for j := 0; j < len(a[i]); j++ {
            fmt.Println(i, j, a[i][j])
        }
    }
}

위 코드는 2 차원 배열을 순회하는 두 가지 방법을 보여줍니다.

go run ~/project/array.go

출력 결과는 다음과 같습니다.

Traversing the Two-Dimensional Array Using the range Keyword
0 0 123
0 1 321
0 2 222
1 0 404
1 1 501
1 2 503
2 0 857
2 1 419
2 2 857

Traversing the Two-Dimensional Array Using Index Numbers
0 0 123
0 1 321
0 2 222
1 0 404
1 1 501
1 2 503
2 0 857
2 1 419
2 2 857

두 방법 모두 동일한 결과를 생성하지만, 근본적으로 다릅니다. 이러한 차이점은 실제로 배열을 사용할 때 특히 명확해질 것입니다.

배열의 실용적인 사용

이전 섹션에서, 배열 순회 (array traversal) 의 두 가지 방법이 근본적으로 다르다고 언급했습니다.

이것을 설명하기 위해 작은 예제를 사용해 보겠습니다.

array.go에 다음 코드를 작성합니다.

package main

import "fmt"

func main() {
    a := [...][]int{{123, 321, 222}, {404, 501, 503}, {857, 419, 857}}
    // Method 1: using the range keyword
    fmt.Println("Traversing the Two-Dimensional Array Using the range Keyword")
    for _, value := range a {
        for _, j := range value {
            fmt.Println(j)
        }
    }
    fmt.Println(a)
    // Method 2: using index numbers
    fmt.Println("\nTraversing the Two-Dimensional Array Using Index Numbers")
    for i := 0; i < len(a); i++ {
        for j := 0; j < len(a[i]); j++ {
            fmt.Println(a[i][j])
            a[i][j] = 0
        }
    }
    fmt.Println(a)
}

위 코드는 두 가지 다른 방법을 사용하여 2 차원 배열의 모든 값을 0으로 설정하는 것을 보여줍니다.

go run ~/project/array.go

프로그램 출력 결과는 다음과 같습니다.

Traversing the Two-Dimensional Array Using the range Keyword
123
321
222
404
501
503
857
419
857
[[123 321 222] [404 501 503] [857 419 857]]

Traversing the Two-Dimensional Array Using Index Numbers
123
321
222
404
501
503
857
419
857
[[0 0 0] [0 0 0] [0 0 0]]

range 키워드를 사용하여 배열을 순회할 때, 값을 수정해도 아무런 효과가 없다는 것을 알 수 있습니다. 그러나 인덱스 번호를 사용하여 배열을 순회한 후에는 값을 수정하는 것이 효과적입니다. 이는 range 키워드를 사용하여 배열을 순회할 때, 루프 변수 j가 실제로 배열 값의 **복사본 (copy)**이기 때문입니다. 복사본의 값을 수정하는 것은 원래 배열 a에 영향을 미치지 않습니다. 그러나 인덱스 번호를 사용하여 배열을 순회할 때는 원래 배열의 값을 수정하는 것이 유효합니다.

2 차원 배열의 업스케일링

때로는 3 차원 배열 또는 4 차원 배열을 사용해야 할 필요가 있습니다.

더 높은 차원의 배열로 확장하는 것은 1 차원 배열을 2 차원 배열로 확장하는 것과 크게 다르지 않습니다.

간단한 예제를 살펴보겠습니다.

package main

import "fmt"

func main() {
    a := [2][2][2]int{}
    for i := 0; i < 2; i++ {
        for j := 0; j < 2; j++ {
            for k := 0; k < 2; k++ {
                a[i][j][k] = 1
            }
        }
    }
    fmt.Println(a)
}

위 코드는 3 차원 배열을 정의하고 사용하는 방법을 보여줍니다. 4 차원 또는 더 높은 차원의 배열도 동일한 과정을 거칩니다.

go run ~/project/array.go

출력 결과는 다음과 같습니다.

[[[1 1] [1 1]] [[1 1] [1 1]]]

하지만, 더 높은 차원의 배열은 일반적으로 사용되지 않습니다. 따라서 개념을 이해하는 것으로 충분합니다.

요약

이 랩 (lab) 에서 우리는 다음을 배웠습니다.

  • 2 차원 배열을 정의하는 두 가지 방법
  • 2 차원 배열을 초기화하는 세 가지 방법
  • 2 차원 배열을 순회하는 두 가지 방법
  • 실제 사용에서 두 가지 순회 방법의 차이점
  • 다차원 배열 사용에 대한 개요