위키로그:2014/10/09 - Perl의 해시 참조

From PGWiki

특정 문자열을 구문 분석하여 해시 구조를 만들려고 했다.

예를 들어, "a/b/c"라는 문자열이 있다면 이를 통해

{
  a => 
  {
    b =>
    {
      c => undef
    }
  }
}

이렇게 만들고 싶었다.

하지만 이내 문제에 봉착했다...

예시
# 1. %hash를 정의 & 선언
# 2. 그 심볼의 참조를 $ref에 저장
my %hash = ();
my $ref  = \%hash;

# 3. $hash{a}를 undef로 선언
# 4. $hash{a} 심볼의 참조를 $ref에 저장
$ref->{a} = undef;
$ref = \$ref->{a};

# 5. $hash{a}{b}를 undef로 선언
# 6. $hash{a}{b} 심볼의 참조를 $ref에 저장
$ref->{b} = undef;
$ref = \$ref->{b};

print Data::Dumper->Dump([\%hash]);

$ref = "Success!";

print Data::Dumper->Dump([\%hash]);

결과는 무참히 실패했다.

정확히는 5번 항목에서 실패했으며 아래와 같은 오류가 발생했다.

Not a HASH reference at test1.pl line 34.

간단히 생각해보면 $ref->{b}가 해시를 가리키지 않는다라는 말인데...

혹시나 이상한 주소를 가리키는건가 싶어서 디버거로 확인해봤다.

potatogim@Potato-Laptop:~$ perl -d test1.pl

Loading DB routines from perl5db.pl version 1.33
Editor support available.

Enter h or `h h' for help, or `man perldebug' for more help.

main::(test1.pl:28):	my %hash = ();
DB<1> s
main::(test1.pl:29):	my $ref  = \%hash;
DB<1> 
main::(test1.pl:31):	$ref->{a} = undef;
DB<1> print \%hash
HASH(0x1b7c600)
DB<2> print $ref
HASH(0x1b7c600)
DB<3> s
main::(test1.pl:32):	$ref = \$ref->{a};
DB<3> print \$hash{a}
SCALAR(0x199c048)
DB<4> s         
main::(test1.pl:34):	$ref->{b} = undef;
DB<4> print $ref     
SCALAR(0x199c048)
DB<5> s
Not a HASH reference at test1.pl line 34.
 at test1.pl line 34.
Debugged program terminated.  Use q to quit or R to restart,
  use o inhibit_exit to avoid stopping after program termination,
  h q, h R or h o to get additional info.  
DB<5>

DB<1>, DB<2>에서 보여지듯이 $ref%hash를 가리키고 있다.

이어서 $ref$ref->{a}를 가리키게 한 후에, $ref가 저장한 주소와 \$hash{a}의 주소를 비교해도 같다.

test1.pl의 31, 32번 줄에 작성된 코드에서 $ref->{a}undef의 반환 값을 대입하고 $ref에는 $ref->{a}가 참조하는 메모리 공간의 주소(undef가 위치하는)를 대입했다.

그리고나서 $ref에는 $hash{a}에 저장된 정의되지 않은 값이 들어있을테니, $ref->{b}를 수행하는 시점에서 autovivification이 이루어져서 이 공간에 새로운 익명 해시에 대한 참조가 들어갈 것이라 기대했던 것이다.

하지만, 실상은 아래와 같다.

$ref->{a} = undef;
$ref = \$ref->{a};

${$ref}{b}  = undef; # Error
${$$ref}{b} = undef; # No error

우선 순서의 이해를 위해 참조 방식을 다시 표기해봤다.

$refundef를 저장하는 공간을 가리키고, $$refundef 자체를 가리킨다.

얼라...?

결과적으로 autovivification은 참조 대상이 undef인 경우에 대해 이루어지는데, 오류가 발생한 전자의 경우에는 $refundef를 저장하는 메모리 공간을 가리키기 때문에 오류가 발생한 것이다.

Potatogim (토론) 2014년 11월 7일 (금) 17:09 (KST)



blog comments powered by Disqus