기술적으로 말해서 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를 사용하면 이런 자료 구조 안에 무엇이 들어 있는지를 살펴보는데 도움이 됩니다.