기술적으로 말해서 Perl에는 다차원 배열은 없습니다. 그러나 Perl의 배열이 마치 2차원 이상인 것처럼 동작하도록 할 수 있습니다.

Perl에서 배열의 각 원소는 다른 배열의 레퍼런스일 수 있고, 이것은 마치 2차원 배열처럼 보일 것입니다.

Perl에서 행렬 만들기

다음 코드를 보도록 합시다:

#!/usr/bin/perl
use strict;
use warnings;

my @matrix;

$matrix[0][0] = 'zero-zero';
$matrix[1][1] = 'one-one';
$matrix[1][2] = 'one-two';

우리는 단지 @matrix라는 배열을 생성했습니다. 이것은 Perl에서 평범한 1차원 배열입니다만, 우리는 이것이 마치 2차원 배열인 것처럼 접근하고 있습니다.

다음 라인을 실행하면 어떻게 될까요?

print "$matrix\n";

네, 이 질문은 속임수입니다. 프로그램은 컴파일조차 되지 않고 다음과 같은 에러가 날 것입니다:

Global symbol "$matrix" requires explicit package name at ... line ..
Execution of ... aborted due to compilation errors.

global symbol requires explicit package name 에러에 관한 기사를 읽었다면, 여러분은 이 에러가 여러분이 변수를 선언하지 않았음을 의미하는 것을 아실 겁니다. 이 경우는 $matrix 말이죠. 게다가, Perl에서 배열의 원소에 접근하는 법을 읽으셨다면, 여러분은 @matrix 배열의 첫번째 원소에 접근할 때 $matrix[0]를 사용하는 것을 아실 겁니다. 변수 이름 뒤에 있는 대괄호를 눈여겨보세요!

여기서 다소 혼동스러울 법한 것이 세 가지 있습니다:

@matrix, $matrix[0] 그리고 $matrix가 그것입니다. 처음 두 가지는 서로 연관되어 있습니다. 세번째는 다른 두 가지와 무관합니다. 첫번째 것은 배열입니다. 두번째 것은 배열의 한 원소이고, 세번째 것은 별개의 스칼라입니다. @matrix와 같이 배열을 선언했다면 자동적으로 $matrix[0]을 써서 첫번째 원소에 접근할 수 있습니다. 그러나 여러분이 $matrix도 사용하고 싶다면 이것은 따로 선언해야 합니다.

주의할 점이 있습니다. Perl은 여러분이 배열과 스칼라 변수에 완전히 똑같은 이름을 붙이는 것을 허용하긴 하지만, 하나의 코드 안에서 그렇게 이름이 같은 두 변수를 두지 않는 것을 강력하게 권장합니다. 읽는 사람을 혼란스럽게 할 수 있기 때문입니다.

잠시 옆으로 샜었습니다. 다시 우리 예제로 돌아옵시다:

행렬

다음 코드의 출력이 어떻게 나올지 봅시다:

print "$matrix[0]\n";       # ARRAY(0x814dd90)
print "$matrix[0][0]\n";    # zero-zero
print "$matrix[1][1]\n";    # one-one

첫번째 라인은 ARRAY(0x814dd90)라고 출력합니다. 언급드렸다시피, Perl에는 다차원 배열은 없습니다. 여기서 여러분이 알 수 있는 것은 @matrix 배열의 첫번째 원소가 소위 익명 배열이라고 부르는, 실제 값들을 담고 있는 내부 자료형의 레퍼런스라는 것입니다. ARRAY(0x814dd90)는 매모리 내에 그 자료형의 주소입니다. 여러분이 이 주소를 가지고 딱히 할 수 있는 것은 없고, 단지 이 주소를 "디레퍼런스(de-reference)"할 필요가 생길 거라는 것만 아시면 됩니다. 이 경우에는 또 하나의 대괄호 쌍을 덧붙임으로써 디레퍼런스가 이루어지고 있습니다.

이런 방법으로 여러분은 우리가 배열에 넣었던 원래의 값을 얻어낼 수 있습니다.

다차원 배열을 그리기

Perl에 기본적으로 딸려오는 Data::Dumper라는 모듈을 사용하면 우리가 생성한 행렬을 꽤 읽을 만하게 보여줍니다. 이 모듈을 사용하기 위해서는 먼저 use 구문을 써서 모듈을 메모리에 불러와야 합니다. 그 다음 Dumper 함수를 호출하면서 레퍼런스를 인자로 넘겨주면 됩니다. @matrix 앞에 백슬래쉬 \를 붙이면 이 배열에 대한 레퍼런스가 생성됩니다. Dumper 함수는 자료 구조를 직렬화한 후 문자열의 형태로 반환하고, print 함수가 이것을 받아서 출력합니다.

use Data::Dumper qw(Dumper);
print Dumper \@matrix;

출력은 다음과 같은 형태일 것입니다:

$VAR1 = [
          [
            'zero-zero'
          ],
          [
            undef,
            'one-one',
            'one-two'
          ]
        ];

앞부분의 $VAR1은 단지 Data::Dumper가 사용하는 기본 이름입니다. 여기서는 무시하셔도 됩니다. 나머지 출력에는 세 쌍의 대괄호가 나옵니다. 가장 바깥쪽의 대괄호 쌍은 우리가 @matrix라고 부르는 주 배열을 표현합니다. 안쪽에 있는 첫번째 대괄호 쌍은 하나의 값(zero-zero)을 담고 있습니다. 이것은 행렬의 첫번째 행을 표현하고 있습니다. 안쪽에 있는 두번째 대괄호 쌍은 세 개의 값을 담고 있습니다. 첫번째 값은 undef이고, 이것은 우리가 값을 할당하지 않은 $matrix[1][0]의 자리입니다. 나머지 두 개의 값은 우리가 할당한 값입니다.

2차원 배열이 아닌 그 무엇?

보다시피 이렇게 2차원 배열을 흉내내었습니다만, 이 배열의 모양이 행렬에서 볼 수 있는 직사각형의 형태가 아닙니다. 첫번째 행은 원소가 하나 뿐이고 두번째 행은 세 개입니다(비록 그 중 하나는 undef이지만).

비슷한 원리로 @matrix에는 차원이 아예 없는 원소가 있을 수도 있습니다. 예를 들어 다음과 같이 쓸 수 있습니다:

$matrix[2] = 'two';

이제 Date::Dumper의 출력은 다음과 같이 바뀔 것입니다:

$VAR1 = [
          [
            'zero-zero'
          ],
          [
            undef,
            'one-one',
            'one-two'
          ],
          'two'
        ];

여기서 바깥쪽 배열에는 원소가 세 개 있습니다. 처음 두 개는 내부의 배열들이고, 세번째 것은 단순한 스칼라입니다.

따라서 "행렬"의 "행"들 중 하나는 아예 차원이 존재하지 않습니다.

3차원 이상?

다음 코드를 추가하면 어떻게 될까요?

$matrix[1][3][0] = 130;
$matrix[1][3][1] = 131;

Dumper의 출력은 다음과 같을 것입니다:

$VAR1 = [
          [
            'zero-zero'
          ],
          [
            undef,
            'one-one',
            'one-two',
            [
              130,
              131
            ]
          ],
          'two'
        ];

두번째 내부 배열은 이제 네번째 원소가 생겼고, 그 네번째 원소는 다시 배열(더 정확히는 배열에 대한 레퍼런스)입니다.

결론

Perl의 배열은 몇 "차원"이든 될 수 있고, "반듯한" 형태를 이룰 필요는 없습니다. 배열의 각 원소는 내부의 배열이 될 수 있으며, 그 내부 배열의 각 원소는 다시 내부 배열이 될 수 있고, 이렇게 반복됩니다.

Data::Dumper를 사용하면 이런 자료 구조 안에 무엇이 들어 있는지를 살펴보는데 도움이 됩니다.