Perl Reference Dereference
Perl의 변수 유형
Perl에서는 세 가지 기본 변수 유형이 있습니다. $
, %
, @
를 사용하여 변수를 처리할 수 있습니다. 그러나 복잡한 구조를 나타내기 위해서는 보다 복잡한 코드 구조가 필요합니다. Perl에서는 언어의 부가적인 문법을 알아야 코드 작성이 수월합니다.
기본 사용법
$ 연산자
일반적인 언어에서 숫자 또는 문자열만을 다루는 유형입니다. 스칼라 연산이라고도 하며, 스칼라는 방향성이 없음을 의미합니다. 값 자체를 가져오는 데 사용됩니다.
$var = 1;
$var2 = "string";
$var3 = 333;
print "$var, $var2, $var3";
위와 같이 사용할 수 있습니다.
@ 배열
@
는 배열 타입을 나타내는 명령어입니다. 배열 타입으로 혼동하지 않도록 @
를 사용합니다.
@arr = ("my", "name", "is", "ksoo");
print "$arr[0], $arr[1], $arr[2], $arr[3]\n";
배열 초기화를 위해서는 @
를 사용하여 변수를 선언하고, 원하는 값을 괄호 사이에 넣습니다. 값을 접근하려면 @
대신 $
를 사용합니다.
배열의 길이를 구하려면 @arr
를 사용합니다.
$arrLength = @arr;
print $arrLength;
명확하게 표현하고 싶은 Perl 프로그래머는 다음과 같이 사용할 수 있습니다.
% 해시
%
는 딕셔너리(해시) 타입을 나타냅니다.
%name2email = (
"ksoo" => "ksoo@gmail.com",
"gsoo" => "gsoo@gmail.com",
"young" => "young@gmail.com"
);
print "$name2email{ksoo}\n";
print "$name2email{gsoo}\n";
이러한 구조로 선언은 간단합니다. keys
함수로 키만 뽑아낼 수도 있으며, 뽑아낸 키를 sort
로 정렬도 가능합니다. 저장된 값을 얻어올 때는 $
연산자를 사용해야 합니다. 이는 값 자체가 스칼라이기 때문입니다.
기본을 넘어서
기본만 알고 모든 것을 할 수 있다면 다행이겠지만, 기본을 넘어서야 하는 일도 생깁니다. 여기서는 조금 복잡한 자료구조를 Perl로 표현하는 방법을 살펴봅니다.
배열과 해시는 서로 변환이 가능합니다. 앞서 보았던 코드를 보겠습니다.
%name2email = (
"ksoo" => "ksoo@gmail.com",
"gsoo" => "gsoo@gmail.com",
"young" => "young@gmail.com"
);
@arr = %name2email;
다음과 같은 출력 결과를 확인할 수 있습니다.
$VAR1 = [
'young',
'young@gmail.com',
'ksoo',
'ksoo@gmail.com',
'gsoo',
'gsoo@gmail.com'
];
$VAR1 = {
'young' => 'young@gmail.com',
'ksoo' => 'ksoo@gmail.com',
'gsoo' => 'gsoo@gmail.com'
};
배열의 인덱스 접근은 []
를 이용하고, 해시의 접근은 {}
로 한다는 사실을 기억해야 합니다.
레퍼런스와 복잡한 자료구조
이미 소개된 @
, $
, %
를 이용하여 모든 자료구조를 표현할 수 있을 것 같지만, 한 가지 빠진 조건이 있습니다. Perl은 배열이든 해시든 스칼라 값만을 원소로 가집니다.
예를 들어,
@arr = ("ksoo", "gsoo");
@values = (1, 2, 3, 4, 5);
$arr[1] = @values;
위 코드의 의도했던 바는
$VAR1 = [
'ksoo',
[
1,
2,
3,
4,
5
]
];
와 같을 것입니다. 하지만 결과는 [‘ksoo’, 5] 라는 참혹한 결과를 가지게 됩니다. @values
라는 표현식은 scalar @values
라는 표현식의 줄임이고, scalar @values
는 @values
배열의 크기를 리턴합니다.
우리가 원하는 동작을 하게 하려면 어떻게 해야 할까요? Perl에서는 레퍼런스(\
)가 있습니다. \
뒤에 오는 표현식의 레퍼런스를 반환합니다.
$var1 = "test string";
$var2 = \$var1;
print "$var2\n";
print "${$var2}\n";
print "$$var2\n";
$$var2 = "hello reference";
print "-----------------\n";
print "$var1\n";
결과는 다음과 같습니다.
SCALAR(0x1538050)
test string
test string
-----------------
hello reference
$var2
를 출력하고자 했는데 이 값은 SCALAR의 레퍼런스라고 나옵니다. 레퍼런스의 값을 알기 위해서는 레퍼런스 종류에 대한 기호를 쓰고, {}
으로 감쌉니다. 표현이 명확할 때는 ${$var}
로 쓰지 않고 $$var
로 써도 인식합니다. 그리고 $$var2
를 고쳐본 결과 예상한 대로 원본 $var1
의 값이 바뀌었음을 알 수 있습니다.
@language = ("C", "C++", "Java", "Perl", "python");
$arrayRef = \@language;
print ${$arrayRef}[0];
print $$arrayRef[0];
print $arrayRef->[0];
$arrayRef
는 단순히 @language
의 주소만을 가지고 있습니다. Perl 5.6부터 sigil{var}
는 ->
로 대체 가능합니다. ->
디레퍼런스 연산자는 기존의 sigil
을 이용한 방식보다 훨씬 직관적으로 변수의 값을 읽게 해줍니다.
여기서 C
를 출력하려면 일단은 스칼라이므로 sigil
은 $
가 됩니다.
>$
디레퍼런스 해야 하니까
${$arrayRef}
0번째 요소를 가져와야 하니까
${$arrayRef}[0]
여기서 {}
를 제외하면
$$arrayRef[0]
$arrayRef는 arrayRef->
로 대체 가능하다고 했습니다.
$arrayRef->[0]
익명 배열, 익명 해시
복잡한 sigil
에 앞서 익명 배열과 익명 해시에 대해서 간략하게 알아야 합니다.
$arrayRef = ["C", "C++", "Java", "Perl", "python"];
위에서 썼던 @
을 이용해서 배열을 만들고 레퍼런스를 대입했던 게 기억날 것입니다. 변수를 만들지 않고 바로 대입하려면 익명 배열을 써야 하는데 []
를 사용합니다. []
는 배열을 생성 후 배열의 주소를 리턴합니다.
마찬가지로 익명 해시는 {}
으로, 해시 변수를 생성하고 레퍼런스를 리턴합니다.
조금 더 어려운 내용
$arrayRef=["C++","JAVA","Perl", [5, 4, 3, 2, 1]];
에서 5
를 읽어내고 싶다면 어떻게 할까요? $arrayRef
에 주소값이 들어가는데 그 주소값의 내용은 배열이고, 배열의 안의 3 인덱스 값 또한 주소로 들어가 있습니다. 구조가 바로 머리에 잡혀야 합니다.
먼저 sigil
은 $
$
디레퍼런스해서 3번째 값을 읽어야 하니까
$arrayRef->[3]
여기서 다시 디레퍼런스해서 0번째 값
$arrayRef->[3]->[0]
연속 디레퍼런스일 때는 ->
가 생략이 가능합니다.
$arrayRef->[3][0]
마치 2차원 배열처럼 쓸 수 있습니다.
$arrayRef=["C++","JAVA","Perl", [5, 4, 3, 2, 1]];
print "$arrayRef->[3]->[0]\n";
print "${$arrayRef->[3]}[0]\n";
print "${${$arrayRef}[3]}[0]\n";
print "${$$arrayRef[3]}[0]\n";
print "${$arrayRef->[3]}[0]\n";
print "$arrayRef->[3][0]\n";
6개 모두 같은 표현입니다. 잘 살펴보면 마지막 표현이 얼마나 직관적인지 느낌이 올 것입니다. ->
를 적극적으로 사용하세요.
$object = [ 10, 20, 30, [100, { 300 => [1,2, { 3=>7 } ] } ] ];
이제 $object
에서 마지막 7
을 출력해보겠습니다. 조금 멍해질 수 있는데 정신 차리고 보기 좋게 만들어봅시다.
$VAR1 = [
10,
20,
30,
[
100,
{
'300' => [
1,
2,
{
'3' => 7
}
]
}
]
];
구조가 좀 눈에 보입니다. 일단은 스칼라 값을 가져와야 하니까
$
이제 슬슬 반복하다 보니 익숙해지지 않습니까? 순서대로 3 인덱스, 1 인덱스
$object->[3][1]
‘300’이라는 해시키를 만났으니
$object->[3][1]{300}
해시키의 값은 배열이므로
$object->[3][1]{300}[2]
여기서 다시 해시 디레퍼런스
$object->[3][1]{300}[2]{3}
마침내 7
을 출력합니다.
이 과정을 따라왔다면 아마도 Perl의 레퍼런스 관련된 내용에서 헷갈리더라도 혼자 해결할 수 있는 능력을 갖췄을 것이라 믿습니다. 각종 서적에서 Perl이 쉽다고 하지만 레퍼런스와 디레퍼런스 관련된 내용은 그리 이해하기 쉽지 않으며, 한번에 잘 쓰기도 쉽지 않습니다. 나 또한 헷갈리는 것이 많았고, 나중에 다시 봤을 때 헷갈리지 말자고 정리했습니다. Perl을 배우는 많은 이들에게 도움이 됐으면 합니다.