[Perl] autovivification

perl에서는 autovivification 이라는 강력하고 어찌보면 생소할 수 있는 기능이 있다.

 

autovivification 이란?

깊은 깊이를 갖는 자료구조에서(deep data structure), 선언과 초기화에 필요한 장황한 코드를 사용하지 않고도 초기화를 진행해 주는 기능이다.

다음 예제를 통해 살펴보자.

use Data::Dumper;

my $fruit_count = {};

print "Before: \n";
print Dumper $fruit_count;

$fruit_count->{apple}++;
$fruit_count->{orange}++;

print "After: \n";
print Dumper $fruit_count;

위 예제의 실행 결과는 다음과 같다.

ahn-soohan@Mac-261:fout/perl_practice $ perl autovivification1.pl 

Before: 
$VAR1 = {};
After: 
$VAR1 = {
          'orange' => 1,
          'apple' => 1
        };

선언하지 않은 orange와apple에 대해서 카운터가 1씩 올라가 있는 것을 볼 수 있다.

이처럼 autovivification 은 잘 활용하면 편한 기능일 수도 있겠지만, 의도치 않은 결과를 나타내면서 악마와 같은 기능을 할 수 있는 양날의 검이다.

다음의 예제를 한 번 살펴보자.

use Data::Dumper;

my $members = {};

print "Before: \n";
print Dumper $members;

print "It exists.\n" if exists $members->{ahn}->{items};

print "After: \n";
print Dumper $members;

결과는 다음과 같다.

ahn-soohan@Mac-261:fout/perl_practice $ perl autovivification.pl

Before: 
$VAR1 = {};
After: 
$VAR1 = {
          'ahn' => {}
        };

위의 소스 코드에서는  ahn이라는 필드를 선언한 적이 없는데도 불구하고, 비교에 쓰였다는 이유만으로 해쉬에 ahn이라는 필드가 생성되어있다. 이 기능의 강력함을 좀 더 잘 살펴보기 위해 더욱 deep한 필드에 대한 실험을 해보면,

use Data::Dumper;

my $members = {};

print "Before: \n";
print Dumper $members;

print "fruit exists.\n" if exists $members->{ahn}->{items}->{fruit};
print "vegetable exists.\n" if exists $members->{ahn}->{items}->{vegetable};

print "apple exists.\n" if exists $members->{ahn}->{items}->{fruit}->{apple};
print "potato exists.\n" if exists $members->{ahn}->{items}->{vegetable}->{potato};

print "after: \n";
print Dumper $members;

print "fruit exists.\n" if exists $members->{ahn}->{items}->{fruit};
print "vegetable exists.\n" if exists $members->{ahn}->{items}->{vegetable};

결과는 다음과 같다.

ahn-soohan@Mac-261:fout/perl_practice $ perl autovivification.pl

Before: 
$VAR1 = {};
after: 
$VAR1 = {
          'ahn' => {
                     'items' => {
                                  'fruit' => {},
                                  'vegetable' => {}
                                }
                   }
        };
apple exists.
potato exists.

없던 ahn뿐만 아니라, 하위 필드인 items와 그 밑에 fruit와vegetable 까지 생성된 것을 볼 수 있다. 게다가, 처음 if문에서는 exists에 걸리지 않았던 조건문이 autovivification후에는 exists에 걸려서 코드의 동작이 달라진 것을 볼 수 있다. 이런 경우는 보통은 개발자가 의도하지 않은 경우가 많으므로, 이런 부분에 대해서 주의를 할 필요가 있다.

autovivification을 사용하지 않도록 하기 위하여

아래 코드 한줄을 추가해주면 autovivification이 일어나지 않도록 할 수 있다.

no autovivification;

위 예제에 이 코드를 추가하여 실행하여 보면 아래와 같은 결과를 얻을 수 있다.

  • 코드:
    use Data::Dumper;
    
    no autovivification; # here!
    
    my $members = {};
    
    print "Before: \n";
    print Dumper $members;
    
    print "apple exists.\n" if exists $members->{ahn}->{items}->{fruit};
    print "potato exists.\n" if exists $members->{ahn}->{items}->{vegetable};
    
    print "apple exists.\n" if exists $members->{ahn}->{items}->{fruit}->{apple};
    print "potato exists.\n" if exists $members->{ahn}->{items}->{vegetable}->{potato};
    
    print "after: \n";
    print Dumper $members;
    
    print "apple exists.\n" if exists $members->{ahn}->{items}->{fruit};
    print "potato exists.\n" if exists $members->{ahn}->{items}->{vegetable};
  • 결과:
    ahn-soohan@Mac-261:fout/perl_practice $ perl autovivification.pl 
    
    Before: 
    $VAR1 = {};
    after: 
    $VAR1 = {};