Perl에서 키가 주어진 배열에서 나오는 해시를 어떻게 생성합니까?


80

배열이 있다고 가정 해 봅시다. "배열에 X가 포함되어 있습니까?"라는 작업을 많이 할 것입니다. 체크 무늬. 이를 수행하는 효율적인 방법은 해당 배열을 해시로 바꾸는 것입니다. 여기서 키는 배열의 요소입니다. 그러면 다음과 같이 말할 수 있습니다.

if ($ hash {X}) {...}

이 배열에서 해시로 변환하는 쉬운 방법이 있습니까? 이상적으로는 익명 배열을 취하고 익명 해시를 반환 할 수있을만큼 다재다능해야합니다.

답변:


120
%hash = map { $_ => 1 } @array;

"@hash {@array} = ..."솔루션만큼 짧지는 않지만 이러한 솔루션은 해시와 배열이 이미 다른 곳에 정의되어 있어야하지만이 솔루션은 익명 배열을 가져와 익명 해시를 반환 할 수 있습니다.

이것이하는 일은 배열의 각 요소를 "1"과 쌍으로 만드는 것입니다. 이 (키, 1, 키, 1, 키 1) 쌍 목록이 해시에 할당되면 홀수 번호가 해시 키가되고 짝수 번호가 각각의 값이됩니다.


43
 @hash{@array} = (1) x @array;

해시 조각, 해시의 값 목록이므로 앞에 list-y @가 표시됩니다.

에서 워드 프로세서 :

'%'대신 해시 슬라이스에 '@'를 사용하는 이유에 대해 혼란 스러우면 다음과 같이 생각하십시오. 대괄호 유형 (정사각형 또는 곱슬 곱슬 함)은 배열인지 해시인지 여부를 결정합니다. 반면에 배열 또는 해시의 선행 기호 ( '$'또는 '@')는 단수 값 (스칼라) 또는 복수 값 (목록)을 반환하는지 여부를 나타냅니다.


1
와우, 나는 그것을 들어 본 적이 없다. 감사! 작동 방식을 이해하는 데 문제가 있습니다. 설명을 추가 할 수 있습니까? 특히, % hash라는 이름의 해시를 어떻게 @ 기호로 참조 할 수 있습니까?
raldi

2
raldi : 해시 조각, 해시의 값 목록이므로 앞에 list-y @가 표시됩니다. 참조 perldoc.perl.org/perldata.html#Slices을 - 특히 마지막 부분의 단락
ysth

답변에 추가해야합니다!
raldi

RHS도 설명해 주시겠습니까? 감사.
Susheel Javadi 2010

1
(list) x $ number는 목록을 $ number 번 복제합니다. 스칼라 컨텍스트에서 배열을 사용하면 요소 수가 반환되므로 (1) x @array는 @array와 길이가 같은 1의 목록입니다.
moritz 2010

39
@hash{@keys} = undef;

여기서 해시를 참조하는 구문 @은 해시 슬라이스입니다. 기본적으로 $hash{$keys[0]}AND $hash{$keys[1]}AND $hash{$keys[2]}...는 =, lvalue의 왼쪽에있는 목록이고, 실제로 해시로 들어가 모든 명명 된 키에 대한 값을 설정하는 해당 목록에 할당합니다. 이 경우에는 하나의 값만 지정 했으므로 해당 값은에 들어가고 $hash{$keys[0]}다른 해시 항목은 모두 정의되지 않은 값으로 자동 활성화 (활성화)됩니다. [여기에서 내 원래 제안은 식 = 1로 설정되었는데, 하나의 키를 1로 설정하고 다른 키를 undef. 일관성을 위해 변경했지만 아래에서 볼 수 있듯이 정확한 값은 중요하지 않습니다.]

=의 왼쪽에있는 표현식 인 lvalue가 해시로 만들어진 목록이라는 것을 알게되면, 우리가 그것을 사용하는 이유가 이해되기 시작할 것입니다 @. [Perl 6에서 변경 될 것이라고 생각합니다.]

여기서 아이디어는 해시를 세트로 사용한다는 것입니다. 중요한 것은 내가 부여하는 가치가 아닙니다. 키의 존재 일뿐입니다. 그래서 당신이 원하는 것은 다음과 같은 것이 아닙니다.

if ($hash{$key} == 1) # then key is in the hash

대신 :

if (exists $hash{$key}) # then key is in the set

실제로 exists해시의 값을 사용하는 것보다 검사를 실행하는 것이 더 효율적입니다 .하지만 여기에서 중요한 것은 해시의 키만으로 집합을 표현한다는 개념입니다. 또한 누군가는 undef여기에서 값 을 사용 하면 값을 할당하는 것보다 저장 공간을 덜 소비하게 된다고 지적했습니다 . (또한 값이 중요하지 않으므로 내 솔루션은 해시의 첫 번째 요소에만 값을 할당하고 다른 요소는 남겨두고 혼란을 덜 발생시킵니다. undef다른 솔루션은 수레 바퀴를 돌려 값의 배열을 구축합니다. 해시; 완전히 낭비되는 노력).


1
이것은 해시를 초기화하는 임시 목록을 만들지 않기 때문에 다른 것보다 선호됩니다. 이것은 더 빠르며 적은 메모리를 소비해야합니다.
Leon Timmermans

1
Frosty : 먼저 "my % hash"를 선언 한 다음 "@hash {@arr} = 1"( "my"아님)을 수행해야합니다.
Michael Carman

8
= (), 아니 = undef, 첫 번째 이후의 모든 값이 아닌 모든 값에 대해 암시 적으로 undef를 사용하는 일관성을 위해. (이 주석에서 설명했듯이를보고 undef1로 변경하고 모든 해시 값에 영향을 미칠 수 있다고 생각 하기가 너무 쉽습니다 .)
ysth

2
값이 여기에서 "undef"로 끝나기 때문에 (그리고 아마도 당신이 생각하는 이유가 아닙니다-ysth가 지적했듯이) "if ($ hash {$ value})"와 같은 코드에서 해시를 사용할 수 없습니다. "if (exists $ hash {$ value})"가 필요합니다.
Dave Cross

2
해시 값을 실제로로드하여 진실성을 확인하는 것보다 존재 함을 확인하는 것보다 더 효율적이며 undef가 1보다 적은 공간을 차지한다는 점을 지적하도록 답변을 편집하면 좋을 것입니다.
bhollis

16

타이핑 if ( exists $hash{ key } )이 당신에게 너무 많은 일이 아니라면 (관심의 문제가 실제로 그 가치의 진실성보다는 키의 존재이기 때문에 사용하는 것을 선호합니다), 짧고 달콤한 것을 사용할 수 있습니다.

@hash{@key} = ();

8

나는 항상 생각했다

foreach my $item (@array) { $hash{$item} = 1 }

적어도 멋지고 읽기 쉽고 유지 관리가 가능했습니다.


7

여기에는 "배열에 X가 포함되어 있습니까?"를 수행하는 가장 효율적인 방법이라는 전제가 있습니다. 검사는 배열을 해시로 변환하는 것입니다. 효율성은 부족한 자원, 종종 시간, 때로는 공간, 때로는 프로그래머의 노력에 달려 있습니다. 목록과 목록의 해시를 동시에 유지하여 소비되는 메모리를 적어도 두 배로 늘리고 있습니다. 또한 테스트, 문서화 등에 필요한 더 많은 원본 코드를 작성하고 있습니다.

대안보기 목록 :: MoreUtils 모듈에서, 특히 기능으로 any(), none(), true()false(). 그들은 모두 조건 및 인수로 목록 유사으로 블록을 map()하고 grep():

print "At least one value undefined" if any { !defined($_) } @list;

빠른 테스트를 실행하여 / usr / share / dict / words의 절반을 배열 (25000 단어)에로드 한 다음 두 배열을 사용하여 배열의 전체 사전 (5000 번째 단어마다)에서 선택한 11 단어를 찾습니다. -to-hash 메소드와 any()List :: MoreUtils 의 기능.

소스에서 빌드 된 Perl 5.8.8에서 array-to-hash 방법은 방법보다 거의 1100 배 더 빠르게 실행됩니다 any()(Ubuntu 6.06의 패키지 Perl 5.8.7에서 1300 배 더 빠름).

그러나 이것이 전체 이야기는 아닙니다. 배열에서 해시로 변환하는 데 약 0.04 초가 소요되며,이 경우 배열에서 해시로의 시간 효율성이 방법보다 1.5x-2 배 더 빠릅니다 any(). 여전히 좋지만 별 만큼은 아닙니다.

내 직감은 array-to-hash 방법이 any()대부분의 경우 에 이길 것이라는 것입니다. O 각 방법의 알고리즘 분석 등) 필요에 따라 List :: MoreUtils가 더 나은 솔루션이 될 수 있습니다. 확실히 더 유연하고 코딩이 덜 필요합니다. 기억하세요, 조기 최적화는 죄입니다 ... :)


이것은 질문에 대한 답이 아닙니다. 그것은 또한 포인트를 놓칩니다 ... 배열에서 해시로의 변환은 한 번만 발생합니다 ... 총 0.04 초 (2008 년)가 프로그램의 실행 시간에 추가되는 반면 조회는 여러 번 발생합니다.
Jim Balter

2
나는 단지 질문에 답하는 것이 아니라 근본적인 문제를 해결하려고 시도했다. List::MoreUtils사용 사례에 따라 적절한 방법 일 수도 있고 아닐 수도 있습니다. 유스 케이스에는 많은 조회가있을 수 있습니다. 다른 사람들은 그렇지 않을 수도 있습니다. 요점은 배열에서 해시로 변환하고 구성원을 결정 List::MoreUtils하는 근본적인 문제를 해결한다는 것 입니다. 여러 접근 방식을 알면 특정 사용 사례에 가장 적합한 방법을 선택할 수 있습니다.
arclight

5

Perl 5.10에는 마술에 가까운 ~~ 연산자가 있습니다.

sub invite_in {
    my $vampires = [ qw(Angel Darla Spike Drusilla) ];
    return ($_[0] ~~ $vampires) ? 0 : 1 ;
}

여기를 참조하십시오 : http://dev.perl.org/perl5/news/2007/perl-5.10.0.html


1
대형 어레이에 대해 여러 번 수행하면 잠재적으로 훨씬 느려질 수 있습니다.
ysth

1
그것은 "스마트 매치 연산자"입니다 :)
brian d foy

5

또한 가치가 완전성에 대한 지적,이 같은 길이의 배열이 작업을 수행하는 내 일반적인 방법 @keys@vals당신이 선호하는 해시이었다 ...

my %hash = map { $keys[$_] => $vals[$_] } (0..@keys-1);


4
에 대한 일반적인 관용구는 @keys-1입니다 $#keys.
Stefan Majewsky 2012 년

@StefanMajewsky 나는 한동안 실제로 사용하는 것을 보지 못했습니다. 나는 그것에서 멀리 떨어져있다 – 그것은 추하다.
Tamzin Blake 2013

3

Raldi의 솔루션은 다음과 같이 강화할 수 있습니다 (원본의 '=>'는 필요하지 않음).

my %hash = map { $_,1 } @array;

이 기술은 텍스트 목록을 해시로 변환하는 데 사용할 수도 있습니다.

my %hash = map { $_,1 } split(",",$line)

또한 다음과 같은 값이있는 경우 : "foo = 1, bar = 2, baz = 3"다음을 수행 할 수 있습니다.

my %hash = map { split("=",$_) } split(",",$line);

[포함하도록 수정]


제공되는 또 다른 솔루션 (두 줄 사용)은 다음과 같습니다.

my %hash;
#The values in %hash can only be accessed by doing exists($hash{$key})
#The assignment only works with '= undef;' and will not work properly with '= 1;'
#if you do '= 1;' only the hash key of $array[0] will be set to 1;
@hash{@array} = undef;

1
$ _ => 1과 $ _, 1의 차이점은 순전히 문체입니다. 개인적으로 나는 키 / 값 링크를보다 명시 적으로 나타내는 것처럼 보이기 때문에 =>를 선호합니다. @hash {@array} = 1 솔루션이 작동하지 않습니다. 값 중 하나 (@array의 첫 번째 키와 연관된 값) 만 1로 설정됩니다.
Dave Cross

2

Perl6 :: Junction을 사용할 수도 있습니다 .

use Perl6::Junction qw'any';

my @arr = ( 1, 2, 3 );

if( any(@arr) == 1 ){ ... }

1
대형 어레이에 대해 여러 번 수행하면 잠재적으로 훨씬 느려질 수 있습니다.
ysth

1
실제로 한 번하는 것이 훨씬 느립니다. 개체를 만들어야합니다. 그리고 얼마 지나지 않아 해당 개체가 파괴됩니다. 이것은 가능한 것의 예일뿐입니다.
Brad Gilbert

1

집합 이론 연산을 많이하는 경우 Set :: Scalar 또는 유사한 모듈을 사용할 수도 있습니다 . 그런 다음 $s = Set::Scalar->new( @array )세트를 빌드하고 다음 을 사용하여 쿼리 할 수 ​​있습니다 $s->contains($m)..


1

네임 스페이스를 오염시키지 않으려면 코드를 서브 루틴에 배치 할 수 있습니다.

my $hash_ref =
  sub{
    my %hash;
    @hash{ @{[ qw'one two three' ]} } = undef;
    return \%hash;
  }->();

또는 더 나은 방법 :

sub keylist(@){
  my %hash;
  @hash{@_} = undef;
  return \%hash;
}

my $hash_ref = keylist qw'one two three';

# or

my @key_list = qw'one two three';
my $hash_ref = keylist @key_list;

정말로 배열 참조를 전달하고 싶다면 :

sub keylist(\@){
  my %hash;
  @hash{ @{$_[0]} } = undef if @_;
  return \%hash;
}

my @key_list = qw'one two three';
my $hash_ref = keylist @key_list;

%hash = map{ $_, undef } @keylist
Brad Gilbert

1
#!/usr/bin/perl -w

use strict;
use Data::Dumper;

my @a = qw(5 8 2 5 4 8 9);
my @b = qw(7 6 5 4 3 2 1);
my $h = {};

@{$h}{@a} = @b;

print Dumper($h);

제공합니다 (반복 된 키는 배열의 가장 큰 위치에서 값을 얻습니다. 즉, 6이 아닌 8-> 2)

$VAR1 = {
          '8' => '2',
          '4' => '3',
          '9' => '1',
          '2' => '5',
          '5' => '4'
        };

hasref는 여기에서 약간 과장된 것 이상으로 보입니다.
bobbogo

0

정렬 된 연관 배열을 구현 하는 Tie :: IxHash 를 확인할 수도 있습니다 . 이를 통해 데이터 사본 하나에 대해 두 가지 유형의 조회 (해시 및 인덱스)를 모두 수행 할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.