W Perlu, jak utworzyć hash, którego klucze pochodzą z danej tablicy?

Powiedzmy, że mam tablicę i Wiem, że będę robił wiele "czy tablica zawiera X?"czeki. Efektywnym sposobem na to jest przekształcenie tablicy w hash, gdzie klucze są elementami tablicy, a następnie można po prostu powiedzieć

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

Czy istnieje łatwy sposób na konwersję tablicy na hash? Idealnie, powinien być wystarczająco uniwersalny, aby pobrać anonimową tablicę i zwrócić anonimowy hash.

Author: tjwrona1992, 2008-09-18

14 answers

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

Nie jest tak krótki jak " @ hash {@array}=..."rozwiązania, ale te wymagają, aby hash i tablica były już zdefiniowane gdzie indziej, podczas gdy ta może przyjąć anonimową tablicę i zwrócić anonimowy hash.

To, co robi, to bierze każdy element w tablicy i sparuje go z "1". Gdy ta lista par (key, 1, key, 1, key 1) zostanie przypisana do hasha, nieparzyste stają się kluczami hasha, a parzyste stają się odpowiednimi wartościami.

 108
Author: raldi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-21 18:04:33
 @hash{@array} = (1) x @array;

Jest to fragment skrótu, lista wartości z hasha, więc dostaje listę-y @ z przodu.

From The docs:

Jeśli jesteś zdezorientowany, dlaczego używasz a ' @ ' tam na plasterku haszyszu zamiast "%", pomyśl o tym w ten sposób. Na rodzaj wspornika (kwadratowy lub kręcony) reguluje czy jest to tablica czy hash jest oglądany. Z drugiej strony ręka, symbol wiodący ('$'lub'@') na tablicy lub hash wskazuje czy / align = "left" / wartość (Skalar) lub liczba mnoga (lista).

 39
Author: moritz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-20 14:40:14
@hash{@keys} = undef;

Składnia, w której odnosisz się do hasha za pomocą @ jest plasterkiem skrótu. Zasadniczo mówimy $hash{$keys[0]} i $hash{$keys[1]} i $hash{$keys[2]}... jest listą po lewej stronie=, lvalue, i przypisujemy do tej listy, która faktycznie przechodzi do hash i ustawia wartości dla wszystkich nazwanych kluczy. W tym przypadku, podałem tylko jedną wartość, tak, że wartość przechodzi do $hash{$keys[0]}, a inne wpisy hash wszystkie auto-vivify (come to life) z niezdefiniowanymi wartościami. [Moja oryginalna sugestia tutaj ustawiono wyrażenie = 1, które ustawiłoby ten jeden klucz na 1, a pozostałe na undef. Zmieniłem go dla spójności, ale jak zobaczymy poniżej, dokładne wartości nie mają znaczenia.]

Kiedy zdasz sobie sprawę, że lvalue, wyrażenie po lewej stronie=, jest listą zbudowaną z hasha, wtedy zacznie mieć jakiś sens, dlaczego używamy tego @. [Tyle, że myślę, że to się zmieni w Perlu 6.]

Chodzi o to, że używasz hasha jako zestawu. Co? matters nie jest wartością, którą przypisuję; to tylko istnienie kluczy. Więc to, co chcesz zrobić, nie jest czymś takim jak:

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

Zamiast:

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

W rzeczywistości bardziej wydajne jest po prostu uruchamianie exists sprawdzanie, niż zawracanie sobie głowy wartością w hash, chociaż dla mnie ważną rzeczą jest tylko koncepcja, że reprezentujesz zestaw tylko za pomocą kluczy hash. Ktoś zauważył również, że używając undef jako wartości tutaj, będziemy zużywać mniej pamięci przestrzeń niż przydzielilibyśmy wartość. (A także generować mniej zamieszania, ponieważ wartość nie ma znaczenia, a moje rozwiązanie przypisać wartość tylko do pierwszego elementu w hash i pozostawić inne undef, a niektóre inne rozwiązania obracają cartwheels zbudować tablicę wartości, aby przejść do hash; całkowicie zmarnowany wysiłek).

 34
Author: skiphoppy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-04 20:28:36

Zauważ, że jeśli wpisanie if ( exists $hash{ key } ) nie jest dla Ciebie zbyt dużym wysiłkiem (którego wolę używać, ponieważ kwestią zainteresowania jest tak naprawdę obecność klucza, a nie prawdziwość jego wartości), możesz użyć krótkiego i słodkiego

@hash{@key} = ();
 14
Author: Aristotle Pagaltzis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-19 00:37:16

Jest tu założenie, że najskuteczniejszym sposobem zrobienia wielu " czy tablica zawiera X?"checks polega na przekonwertowaniu tablicy na hash. Wydajność zależy od ograniczonego zasobu, często czasu, ale czasami przestrzeni, a czasami wysiłku programisty. Co najmniej podwajasz zużywaną pamięć, zachowując jednocześnie listę i hash listy. Dodatkowo piszesz więcej oryginalnego kodu, który będziesz potrzebował do testowania, dokumentowania itp.

Jako alternatywę, spójrz na List:: moduł MoreUtils, a konkretnie funkcje any(), none(), true() i false(). Wszystkie przyjmują blok jako argument warunkowy i listę jako argument, podobny do map() i grep():

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

Przeprowadziłem szybki test, ładując połowę /usr / share / dict / words do tablicy (25000 słów), a następnie szukając jedenastu słów wybranych z całego słownika (co 5000 słowo) w tablicy, używając zarówno metody array-to-hash, jak i funkcji any() z tablicy. Lista:: MoreUtils.

W Perlu 5.8.8 zbudowanym ze źródła, metoda array-to-hash działa prawie 1100x szybciej niż metoda any() (1300X szybciej pod Ubuntu 6.06 w pakiecie Perl 5.8.7.)

To nie jest jednak pełna historia-konwersja tablicy na hash trwa około 0,04 sekundy, co w tym przypadku zabija efektywność czasową metody array na hash do 1,5 x-2x szybciej niż metoda any(). Nadal dobre, ale nie tak Gwiezdne.

Moje przeczucie jest takie, że array-to-hash metoda będzie pokonać any() w większości przypadków, ale czułbym się o wiele lepiej, gdybym miał jakieś bardziej solidne metryki (wiele przypadków testowych, przyzwoite analizy statystyczne, może jakaś duża analiza algorytmiczna każdej metody, itp.) W zależności od potrzeb List:: MoreUtils może być lepszym rozwiązaniem; z pewnością jest bardziej elastyczny i wymaga mniej kodowania. Pamiętaj, przedwczesna optymalizacja to grzech... :)

 6
Author: arclight,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-19 04:13:37

Zawsze myślałem, że

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

Był co najmniej ładny i czytelny / konserwowalny.

 6
Author: Keith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-21 19:21:22

W Perlu 5.10 istnieje operator zbliżony do magicznego~~:

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

Zobacz tutaj: http://dev.perl.org/perl5/news/2007/perl-5.10.0.html

 5
Author: RET,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-18 23:54:57

Możesz również użyć Perl6:: Junction .

use Perl6::Junction qw'any';

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

if( any(@arr) == 1 ){ ... }
 2
Author: Brad Gilbert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-18 20:27:27

Rozwiązanie Raldiego można dokręcić do tego ("=>"z oryginału nie jest konieczne):

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

Ta technika może być również użyta do zamiany list tekstowych na skróty:

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

Dodatkowo jeśli masz taką linię wartości: "foo=1, bar=2, baz=3" możesz to zrobić:

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

[EDIT to include]


Inne oferowane rozwiązanie (które zajmuje dwie linie) to:

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;
 2
Author: Frosty,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-19 18:53:13

Warto również zwrócić uwagę na kompletność, moja zwykła metoda na zrobienie tego z 2 tablicami o tej samej długości @keys i @vals, które wolisz, by były Hashem...

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

 2
Author: Thom Blake,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-30 20:14:49

Jeśli wykonujesz wiele operacji teoretycznych zbiorów - możesz również użyć Set::Scalar lub podobnego modułu. Następnie $s = Set::Scalar->new( @array ) zbuduje Zestaw dla Ciebie - i możesz go odpytywać za pomocą: $s->contains($m).

 1
Author: zby,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-21 18:32:02

Możesz umieścić kod w podprogramie, jeśli nie chcesz zanieczyszczać swojej przestrzeni nazw.

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

Albo nawet lepiej:

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;

Jeśli naprawdę chcesz przekazać odniesienie do tablicy:

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

my @key_list = qw'one two three';
my $hash_ref = keylist @key_list;
 1
Author: Brad Gilbert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-05 02:00:49

Możesz również sprawdzić Tie::ixhash, który implementuje uporządkowane tablice asocjacyjne. Pozwala to na wykonywanie obu typów wyszukiwania (hash i index) na jednej kopii danych.

 0
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-19 16:47:11
#!/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);

Daje (Uwaga powtarzające się klucze otrzymują wartość na największej pozycji w tablicy - czyli 8 - >2 a nie 6)

$VAR1 = {
          '8' => '2',
          '4' => '3',
          '9' => '1',
          '2' => '5',
          '5' => '4'
        };
 0
Author: Mark Dibley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-14 11:18:07