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로 그 배열에 도시를 추가하죠. 이처럼 참조 덕분에 '해시 값이 리스트'인 구조를 자연스럽게 다룰 수 있어요.

더 알아보기