Perl 참조(reference) 짧은 입문
Perl 참조(reference) 짧은 입문 (perlreftut)
Perl 5에서 가장 중요한 새 기능 중 하나는 다차원 배열이나 중첩 해시 같은 복잡한 데이터 구조를 다루는 능력이에요. 그걸 가능하게 해주는 게 바로 **참조(reference)**인데, 참조는 배열 전체나 해시 전체를 가리키는 하나의 스칼라 값이에요. 해시의 값은 스칼라만 허용된다는 제약이 있는데, 참조를 쓰면 값 하나로 배열 전체를 가리킬 수 있어서 '해시 값이 리스트인' 구조를 흉내 낼 수 있지요. 익숙한 이름과 비유하면, 실제 세포 덩어리 대신 "Ada Lovelace"라는 이름만으로 한 사람을 가리키는 것과 같아요.
출처: perlreftut - Mark's very short tutorial about references
본문
참조를 만들고 쓰는 방법은 각각 딱 두 가지랍니다. 이 네 가지 규칙만 알면 참조로 할 수 있는 중요한 일의 대부분이 해결돼요.
참조 만들기
규칙 1: 변수 앞에 \를 붙이면 그 변수의 참조가 돼요.
$aref = \@array; # $aref는 이제 @array의 참조를 가짐
$href = \%hash; # $href는 이제 %hash의 참조를 가짐
규칙 2: [ ITEMS ]는 새 익명 배열을 만들고 그 참조를 돌려주고, { ITEMS }는 새 익명 해시를 만들고 그 참조를 돌려줘요.
$aref = [ 1, "foo", undef, 13 ]; # $aref는 배열 참조
$href = { APR => 4, AUG => 8 }; # $href는 해시 참조
규칙 2로 얻은 참조는 규칙 1과 같은 종류예요. $aref = [ 1, 2, 3 ];는 아래 두 줄의 축약형일 뿐, 불필요한 @array 변수를 만들지 않는 것뿐이에요.
@array = (1, 2, 3);
$aref = \@array;
[]만 쓰면 새 빈 익명 배열, {}만 쓰면 새 빈 익명 해시를 만들어요.
참조 사용하기
규칙 1: 배열 참조는 중괄호 안에 넣어 배열 이름 대신 쓸 수 있어요. 예를 들어 @array 대신 @{$aref}를 쓰는 거죠.
@a @{$aref} # 배열
reverse @a reverse @{$aref} # 배열 뒤집기
$a[3] ${$aref}[3] # 배열의 한 요소
$a[3] = 17; ${$aref}[3] = 17 # 요소 할당
해시 참조도 정확히 같아요.
%h %{$href} # 해시
keys %h keys %{$href} # 해시의 키 얻기
$h{'red'} ${$href}{'red'} # 해시의 한 요소
$h{'red'} = 17 ${$href}{'red'} = 17 # 요소 할당
"참조만 있고 배열을 순회하려면?" 같은 질문은, 원래 배열로 쓸 코드를 적고 그 배열 이름만 {$reference}로 바꾸면 돼요. 예를 들어 배열 순회는 @array 대신 @{$aref}를 쓰면 되고, 해시 출력은 keys %hash 대신 keys %{$href}를 쓰면 되죠.
규칙 2: 단일 요소를 꺼낼 때는 화살표 표기로 간결하게 써요. ${$aref}[3]은 읽기 어려우니 $aref->[3]으로, ${$href}{red}는 $href->{red}로 써요.
$aref->[3] # 배열의 4번째 요소
$href->{'red'} # 해시의 'red' 요소
주의할 점은 $aref->[3]과 $aref[3]이 완전히 다르다는 거예요. 후자는 이름이 비슷한 다른 배열 @aref의 4번째 요소예요. $aref와 @aref는 $item과 @item이 서로 무관한 것처럼 무관하답니다.
화살표 규칙과 다차원 배열
두 개의 첨자(subscript) 사이에서는 화살표를 생략할 수 있어요. $a[1]->[2]는 $a[1][2]`로 쓸 수 있고, 뜻은 같아요.
@a = ( [1, 2, 3], [4, 5, 6], [7, 8, 9] );
@a는 요소가 셋인 배열이고, 각 요소는 또 다른 배열의 참조예요. $a[1][2]는 그 중 6을 가리켜요. 이렇게 쓰면 마치 2차원 배열처럼 보이지요? $a[ROW]->[COLUMN]으로 어떤 행·열의 요소든 읽거나 쓸 수 있어요. 화살표가 없었다면 ${$a[1]}[2]처럼 읽기 힘들게 써야 했을 거예요. 3차원은 $x[2][3][5]로 깔끔하게 표현돼요.
참조와 관련된 소소한 사실들
- 참조가 가리키는 배열을 복사하려면
$aref2 = [@{$aref1}];처럼[...]로 새 익명 배열을 만들면 돼요.$aref2 = $aref1;은 같은 배열을 가리키는 참조 두 개를 만드는 것뿐이에요(복사가 아니에요). - 해시 복사는
$href2 = {%{$href1}};로 해요. - 어떤 변수가 참조인지 보려면
ref함수를 써요. 해시 참조면HASH, 배열 참조면ARRAY를 돌려줘요. - 실수로 참조를 문자열처럼 출력하면
ARRAY(0x80f5dec)나HASH(0x826afc0)같은 문자열이 보여요. 이게 보이면 참조를 출력해버린 거랍니다.
my %table;
while (<>) {
chomp;
my ($city, $country) = split /, /;
$table{$country} = [] unless exists $table{$country};
push @{$table{$country}}, $city;
}
for my $country (sort keys %table) {
print "$country: ";
my @cities = @{$table{$country}};
print join ', ', sort @cities;
print ".\n";
}
위 코드는 국가 이름을 키로, 도시 이름 배열의 참조를 값으로 갖는 해시를 만들어 입력을 정리해요. $table{$country} = []로 처음 봤을 때 빈 익명 배열을 만들고, push @{$table{$country}}, $city로 그 배열에 도시를 추가하죠. 이처럼 참조 덕분에 '해시 값이 리스트'인 구조를 자연스럽게 다룰 수 있어요.