it-swarm-tr.com

Perl'de anahtarları belli bir diziden gelen bir hash'ı nasıl oluşturabilirim?

Diyelim ki bir dizi var ve çok fazla "Dizi X içeriyor mu?" Yapacağımı biliyorum. denetler. Bunu yapmanın etkili yolu, bu diziyi bir diziye çevirmek, anahtarların dizinin öğeleri olduğu ve sonra söyleyebilirsiniz. 

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

Bu dizi-hash dönüşümünü yapmanın kolay bir yolu var mı? İdeal olarak, isimsiz bir dizi almak ve isimsiz bir hash döndürmek çok yönlü olmalıdır.

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

"@Hash {@array} = ..." çözümleri kadar kısa değil, ama bu olanlar karma ve dizinin başka bir yerde tanımlanmasını gerektirir, oysa bu isimsiz bir dizi alabilir ve isimsiz bir karma getirebilir.

Bunun yaptığı şey, dizideki her öğeyi alıp "1" ile eşleştirmektir. Bu (anahtar, 1, anahtar, 1, anahtar 1) çiftlerinin bir listesi bir karma değere atandığında, tek sayılı olanlar karma değerleri olur ve çift sayılı olanlar ilgili değerler haline gelir.

109
raldi
 @hash{@array} = (1) x @array;

Bir karma dilimi, karma değerlerinin bir listesi, bu yüzden list-y @ önünü alır.

Dokümanlar :

Neden kullandığınız konusunda kafanız karışıyorsa. orada bir '@' bir karma dilim yerine. Bir '%', böyle düşünün. dirsek türü (kare veya kıvırcık) Bir dizi mi yoksa bir karakter mi olduğunu yönetir. bakılıyor. Diğer yandan El, baştaki sembol ('$' veya '@') dizi ya da karma olup olmadığını gösterir. tekil bir değer kazanıyorsun (bir skalar) veya çoğul (bir liste).

41
moritz
@hash{@keys} = undef;

Burada, @ olan bir hashtan bahsettiğiniz sözdizimi bir karma dilimdir. Temel olarak $hash{$keys[0]} AND $hash{$keys[1]} AND $hash{$keys[2]} ... diyoruz ki, =, bir lvalue'nin sol tarafındaki bir listedir ve o listeye atarız ki bu aslında karmaya gider ve tüm değerler için değerleri belirler. adlandırılmış anahtarlar. Bu durumda, sadece bir değer belirledim, böylece bu değer $hash{$keys[0]} 'ye giriyor ve diğer karma girdiler tanımsız değerlerle otomatik olarak canlanıyor (canlanıyor). [Buradaki orijinal önerim, 1 anahtarını = 1 olarak ayarlamıştı; bu, bir anahtarı 1'e ve diğerlerini undef olarak ayarlayacaktı. Tutarlılık için değiştirdim, ancak aşağıda göreceğimiz gibi, kesin değerler önemli değil.]

Değerin, = 'nin sol tarafındaki ifadenin karma değerin bir listesi olduğunu anlayınca, o zaman neden bu @ kullandığımızı biraz anlamaya başlayacağız. [Bunun Perl 6'da değişeceğini düşünüyorum hariç]

Buradaki fikir, karma kümesi kullandığınızdır. Önemli olan atadığım değer değil; bu sadece anahtarların varlığı. Yani yapmak istediğin şey şunun gibi değil:

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

yerine:

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

Aslında sadece exists kontrolünü çalıştırmak, hashtaki değerle uğraşmaktan daha etkilidir, ancak burada benim için önemli olan şey sadece kümenin anahtarlarıyla bir kümeyi temsil ettiğiniz kavramdır. Ayrıca, biri burada undef değerini kullanarak, değer atadığımızdan daha az depolama alanı kullanacağımıza dikkat çekti. (Ayrıca, değer önemli olmadığı için daha az karışıklık yaratır ve benim çözümüm sadece hashtaki ilk elemana bir değer verir ve diğerlerini undef bırakır ve diğer bazı çözümler gitmek için bir değerler dizisi oluşturmak için çember çeviriyorlar. karma içine; tamamen boşa harcanan çaba).

34
skiphoppy

if ( exists $hash{ key } ) yazmanın sizin için çok fazla çalışmadığını unutmayın (ilgilendiğim mesele gerçekten değerinin doğruluğu yerine bir anahtarın varlığıdır), kısa ve tatlı kullanabileceğinizi unutmayın.

@hash{@key} = ();
15

Bunu hep düşündüm 

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

en azından güzel ve okunabilir/bakımı yapılabilir.

7
Keith

Burada bir ön varsayım var, "dizi X içeriyor mu?" denetler diziyi bir karma değerine dönüştürmektir. Verimlilik, kıt kaynağa, genellikle zamana, bazen de alana ve bazen de programcının çabalarına bağlıdır. En azından bir listeyi ve listenin bir karma değerini aynı anda tutarak, tüketilen hafızayı iki katına çıkarırsınız. Ayrıca, test etmeniz, belgelendirmeniz vb. İçin daha orijinal kodlar yazıyorsunuz.

Alternatif olarak, List :: MoreUtils modülüne, özellikle any(), none(), true() ve false() işlevlerine bakın. Hepsi koşullu bir blok, argüman olarak map() ve grep() gibi bir liste alır:

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

Hızlı bir test yaptım,/usr/share/dict/words'in yarısını bir diziye yükledim (25000 kelime), ardından her iki diziyi kullanarak dizideki tüm sözlükten (her 5000 kelimeden) seçilen on bir kelimeyi aradım. -to-hash yöntemi ve List :: MoreUtils öğesinden any() işlevi.

Kaynaktan derlenmiş Perl 5.8.8'de, diziden-hash yöntemi any() yönteminden neredeyse 1100x daha hızlı çalışır (Ubuntu 6.06'nın paketlenmiş Perl 5.8.7 altında 1300x daha hızlı)

Ancak tam hikaye bu değil - dizi-hash dönüşümü yaklaşık 0.04 saniye sürer, bu durumda dizi-karma yönteminin zaman verimini any() yönteminden 1.5x-2x daha hızlı öldürür. Hala iyi, ama neredeyse yıldız gibi değil.

Benim içgüdüsel hissim dizi-hash yönteminin çoğu durumda any() işlevini geçeceği yönündedir, ancak daha katı ölçütlerim olsaydı daha iyi hissederdim (birçok test vakası, uygun istatistiksel analizler, belki biraz büyük -Onların her birinin algoritmik analizi, vs.) İhtiyaçlarınıza bağlı olarak, List :: MoreUtils daha iyi bir çözüm olabilir; kesinlikle daha esnektir ve daha az kodlama gerektirir. Unutmayın, erken optimizasyon günahtır ... :)

7
arclight

Perl 5.10'da büyüye yakın ~~ işleci var:

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

Buraya bakın: http://dev.Perl.org/Perl5/news/2007/Perl-5.10.0.html

5
RET

Ayrıca Perl6 :: Junction 'yi de kullanabilirsiniz.

use Perl6::Junction qw'any';

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

if( any(@arr) == 1 ){ ... }
2
Brad Gilbert

Ayrıca bütünlüğü dikkate almaya değer, bunu yapmak için her zamanki yöntemim, 2 aynı uzunluktaki diziyle @keys ve @vals tercihini yapmak.

my %hash = map { $keys[$_] => $vals[$_] } ([email protected]);

2
Tamzin Blake

Raldi'nin çözümü buna kadar sıkılaştırılabilir (orijinalden '=>' gerekli değildir):

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

Bu teknik, metin listelerini karma değerlere dönüştürmek için de kullanılabilir:

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

Ayrıca şöyle bir değer satırınız varsa: "foo = 1, bar = 2, baz = 3" şunları yapabilirsiniz:

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

[Dahil etmek için EDIT]


Sunulan başka bir çözüm (iki satır alan):

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
Frosty

Çok sayıda teorik işlem yaparsanız - Set :: Scalar veya benzer modüllerini de kullanabilirsiniz. Daha sonra $s = Set::Scalar->new( @array ), Küme sizin için inşa eder - ve şunu sorgulayabilirsiniz: $s->contains($m).

1
zby

Ad alanınızı kirletmek istemiyorsanız, kodu bir alt yordama yerleştirebilirsiniz.

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

Veya daha da iyisi:

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;

Bir dizi referansını gerçekten geçmek istiyorsanız:

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

my @key_list = qw'one two three';
my $hash_ref = keylist @key_list;
1
Brad Gilbert
#!/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);

verir (tekrarlanan tuşların değeri dizideki en büyük pozisyona getirme - yani 8-> 2 ve 6 değil)

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

Ayrıca sipariş edilen ilişkisel dizileri uygulayan Tie :: IxHash 'ı da kontrol etmek isteyebilirsiniz. Bu, verilerinizin bir kopyasında her iki arama türünü de (karma ve dizin) yapmanızı sağlar.

0
Dave G