Recent Posts
Recent Comments
11-29 16:02
관리 메뉴

동글동글 라이프

역 해시 (Reverse Hash) 본문

개발자 이야기/Perl

역 해시 (Reverse Hash)

동글동글라이프 2009. 1. 11. 18:29

- Hash

사용되는 키(Key) 와 값(value)을 가지는 구조인 hash는 Perl에서 아래와 같이 구성된다.


1
2
3
4
5
6
7
8
9
10
11
12
13
use strict; 
my %hash1 = ( 
	a => 1, 
	b => 1, 
	c => 2
); 
my %hash2 = ( 
	'a' , 1, 'b' , 1, 'c' , 2
); 
print "Print hash1\n";
print "$_ = $hash1{$_} \n",for(keys %hash1);
print "Print hash2\n";
print "$_ = $hash2{$_} \n",for(keys %hash2);


Output:

1
2
3
4
5
6
7
8
Print hash1
c = 2 
a = 1 
b = 1 
Print hash2
c = 2 
a = 1 
b = 1 

hash1과 hash2는 표현방법이 다를 뿐 같은 값을 담고 있으며

일반전인 hash 구성 방법이다.

a 라는 key 값에 1이라는 값이 쌍으로 대입되어 있으며,

이러한 해시구조는 데이터를 관리하거나 표현할 때 유용하게 사용이 된다.


Key 값을 통해서 Value 값을 찾는것은 매우 간단하다.

Hash의 특정 요소를 접근하려면 해쉬의 키를 { } 을 사용하여 원하는 요소를 접근할 수 있다.

위의 예제에서 key값이 a인 값의 value를 알고 싶을 때

print $hash1{'a'};     # 1

라고 출력하면 1 이라는 결과값이 출력이 된다.


기본적으로 hash를 배우는 사람들은 여기서 만족을 한다.

하지만 질문을 하나 던져 보도록 하자.



Value값에 따라 Key 값을 구하고 싶을 때는 어떻게 하는 것이 좋을까?


첫 번째 답안 제출 : 해당 값(Value)를 hash안에서 Key 값과 일치하는것을 모두 검색해서 찾으면 Get !!

허접한 설명 : 검색자체에 시간을 너무 많이 뺏김으로 좋은 알고리즘이 아니다. 시간은 생명!!

두 번째 답안 제출 : $hash{'키'} 니깐...  <-->  $hash1{'값'} 맞나염 ????

허접한 설명 : 넌 hash가 뭔지도 모르는 놈이다 -_- 펄독보고 내공 좀 더 쌓아라...

세 번째 답안 제출 : 역 해시를 만들면 됩니다.

허접한 설명 : Oh!! Good Job~


그렇다... 해시를 처음 만들 때 역해시도 같이 만들어 놓으면 되는것이다.

여기서 역 해시란 원래 해시에서 Key와 Value가 바뀌어서 들어간 해시를 뜻한다.





 
  - Perl hash 문제1 ! 

  역해시를 만들어주는 코드를 작성하여라.

 



지나가는 손님1 :  my %rhash = reverse %hash;    # 한방에 해결? 후욱후욱 내공 주삼

허접한 설명 : 상당히 좋은 방법이다. 일단 hash란 배열처럼 차례대로 데이터를 넣으면
 
                   첫번째는 key 두번째는 value로 키와 값이 쌍으로 대입되므로 reverse로 뒤집어 주기만 하면,

                   간단하게 역해시가 완성이 된다.


나 펄좀 공부했삼 :  태클!! 저런식으로 역해시를 만들때 한가지 문제점이 있다. Key 값은 중복 될 수 없으니

                           역해시를 만들때 value를 키로 사용하면 값을 잃어버릴 수도 있게 된다.



문제점을 파악하였는가?

나쁘지 않은 발상 및 코드지만... 중복된 값이 없다는 가정하에 작성 될 수 있는 코드이다.

앞서 작성한 예제를 코드에 적용시켜 역해시를 만들어 보겠다.


1
2
3
4
5
6
7
8
9
10
use strict; 
my %hash = ( 
	a => 1, 
	b => 1, 
	c => 2
); 
my %rhash = reverse %hash;

print "Print rhash\n";
print "$_ = $rhash{$_} \n",for(keys %rhash);


Output:

1
2
3
Print rhash
1 = a 
2 = c 

2 = c 부터 차례차례 reverse가 되었을 때,

1 = b 의 값을 1 = a 로 값이 치환되어 버린것을 확인 할 수 있다.





여기서 Perl 커뮤니티의 대부 a3r0님의 문제 작렬!!

 
  - Perl hash 문제2 ! 
 

   %hash = ( a=>1, b=>1, c=>2); 을 역해시화 하면

   %rhash = ( 1=>['a','b'], 2=>['c'] ); 처럼 되게 만들어보아라!
 




역해시는 이와 같이 값이 중복 될 때는 기존의 key를 익명 배열에 넣어서 Push 해 버리는 방법을 사용하여

중복된 value가 있더라도 그 값이 유실 되지 않도록 할 수 있다.



일단 직접 만들어 보는것이 좋지만...


모범답안을 공개하도록 한다.



처음 답안을 공개했던 Luz♡lunA 님의 코드



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use strict;
use warnings;
my %hash = ( a=>1, b=>1, c=>2);
my %rhash;

print "print hash\n";
while ( my ($key,$value) = each %hash ) {
	print "$key : $value\n";
	push @{$rhash{$value}},$key;
}

print "print reverse hash\n";
while ( my ($key,$value) = each %rhash ) {
	print "$key : @{$value} \n";	 
}


Output:

1
2
3
4
5
6
7
print hash
c : 2
a : 1
b : 1
print reverse hash
1 : a b 
2 : c 



@{배열래퍼런스} 로 디 레퍼런스 하는 부분에서 깔끔하게 처리해 주셨다.


뒤이어 pung96님의 답안 소스를 조금더 perlish 하게 꾸며 주었다.



1
2
3
4
5
6
7
8
9
10
11
use strict;
use warnings;
my %hash = ( a=>1, b=>1, c=>2);
my %rhash;

my ($k,$v);
push @{$rhash{$v}}, $k while ( ($k, $v) = each %hash );
print "print reverse hash\n";
while ( my ($key,$value) = each %rhash ) {
	print "$key : @{$value} \n";	 
}


Output:

1
2
3
print reverse hash
1 : a b 
2 : c 



뒤늦게 keys 함수를 사용한 keedi님의 깔끔한 답변~


1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/perl 

use strict;
use warnings;
use Data::Dumper;

my %hash = ( a => 1, b => 1, c => 2 );

my %rhash;
push @{ $rhash{$hash{$_}} }, $_ for keys %hash;

print Dumper(\%hash);
print Dumper(\%rhash);


Output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$VAR1 = {
          'c' => 2,
          'a' => 1,
          'b' => 1
        };
$VAR1 = {
          '1' => [
                   'a',
                   'b'
                 ],
          '2' => [
                   'c'
                 ]
        };


코어모듈인 Date::Dumber를 사용하여 보기좋게 출력하였다.


a3r0님은 Data::Dump 모듈이 더 출력이 깔끔하다고 이야기 하셨지만


코어모듈이 아니라는 단점이...


뭐.. 모듈 설치야 간단하지만 Codepad에서 Output이 출력이 되지 않는다는 점이 아쉽다.






자 그러면... 여기까지 왔으면 조금만 더 힘을 내자.


이렇게 만든 역해시를 다시 원래 해시로 돌려주는 방법은 무엇일까?




 
  - Perl hash 문제3 ! 

 
%rhash = ( 1=>['a','b'], 2=>['c'] ); 를

  %hash = ( a=>1, b=>1, c=>2 ); 로 만들어라.







본인의 경우는 2중루프를 돌려 이 문제를 풀었다.


use strict;
use warnings;
use Data::Dumper;

my %hash = ( a => 1, b => 1, c => 2 );

my %rhash;
push @{ $rhash{$hash{$_}} }, $_ for keys %hash;
my ($k,$v);
my %dhash;

foreach my $key( keys %rhash){
		foreach(@{$rhash{$key}}){
			$dhash{$_} = $key;
		}
}

print Dumper(\%hash);
print Dumper(\%rhash);
print Dumper(\%dhash);


Output:

$VAR1 = {
          'c' => 2,
          'a' => 1,
          'b' => 1
        };
$VAR1 = {
          '1' => [
                   'a',
                   'b'
                 ],
          '2' => [
                   'c'
                 ]
        };
$VAR1 = {
          'c' => '2',
          'a' => '1',
          'b' => '1'
        };



각각의 무명배열의 값을 따라간 뒤  key와 value를 다시 넣어주는 작업을 하였는데,

perl 커뮤니티에서 이러한 답을 내면 --(점수를 잃는의미)를 받게 된다.



모범답안을 제시하자면


a3r0님의 해시 슬라이스 무공을 사용한... 답안;;


1
2
3
4
5
6
7
use strict; 
use warnings;
use Data::Dump qw/dump/; 
my %rhash = ( 1=>['a','b'], 2=>['c'] ); 
my %hash; 
@hash{ @{$rhash{$_}} }=($_)x@{$rhash{$_}} for keys %rhash; 
dump(%hash); 


결과값 :  ("c", 2, "a", 1, "b", 1)


dump함수가 %hash를 이쁘게 출력시켜준다.


다음번엔 여기서 사용된 해시 슬라이스 신공에 대해서 한번 블로깅을 해야 할듯...




역해시를 구현해야 하시는 분들께 좋은 자료가 되길!



Good Job~ ^^/


'개발자 이야기 > Perl' 카테고리의 다른 글

Morse (모스 부호)  (9) 2009.01.29
Perl로 짠 IRC Bot  (1) 2009.01.13
irc 로그 분석  (0) 2009.01.08
정규 표현식에 대하여...  (6) 2008.12.29
플래시 게임을 즐기자.  (6) 2008.12.11
Comments