配列があり、「配列にはXが含まれていますか?」をたくさん実行することがわかっているとします。チェックします。これを行う効率的な方法は、その配列をハッシュに変換することです。ここで、キーは配列の要素であり、次のように言うことができます。
if($ hash {X}){...}
この配列からハッシュへの変換を行う簡単な方法はありますか?理想的には、匿名配列を取得して匿名ハッシュを返すのに十分な汎用性が必要です。
%hash = map { $_ => 1 } @array;
「@hash{@array}= ...」ソリューションほど短くはありませんが、これらのソリューションでは、ハッシュと配列がすでに別の場所で定義されている必要がありますが、これは匿名配列を取得して匿名ハッシュを返すことができます。
これは、配列内の各要素を取得し、それを「1」とペアにすることです。この(key、1、key、1、key 1)ペアのリストがハッシュに割り当てられると、奇数番号のペアがハッシュのキーになり、偶数番号のペアがそれぞれの値になります。
@hash{@array} = (1) x @array;
これは、ハッシュからの値のリストであるハッシュ スライスであるため、前に list-y @ を取得します。
ドキュメントから:
'%' の代わりにハッシュ スライスで '@' を使用する理由について混乱している場合は、次のように考えてください。括弧のタイプ (四角か中かっこ) によって、それが配列かハッシュかが決まります。一方、配列またはハッシュの先頭の記号 ('$' または '@') は、単一の値 (スカラー) を取得するか、複数の値 (リスト) を取得するかを示します。
@hash{@keys} = undef;
でハッシュを参照しているここでの構文@
は、ハッシュスライスです。基本的に、$hash{$keys[0]}
AND $hash{$keys[1]}
AND $hash{$keys[2]}
...は、左辺値である=の左側にあるリストであり、そのリストに割り当てます。このリストは、実際にはハッシュに入り、すべての名前付きキーの値を設定します。この場合、私は1つの値のみを指定したので、値はに$hash{$keys[0]}
なり、他のハッシュエントリはすべて未定義の値で自動活性化(復活)します。[ここでの私の最初の提案は、式= 1に設定されていました。これにより、1つのキーが1に設定され、他のキーがに設定されundef
ます。一貫性を保つために変更しましたが、以下で説明するように、正確な値は重要ではありません。]
=の左側にある式である左辺値がハッシュから構築されたリストであることに気付くと、なぜそれを使用しているのかが理解できるようになります@
。[これはPerl6で変更されると思うことを除いて。]
ここでの考え方は、ハッシュをセットとして使用しているということです。重要なのは、私が割り当てている値ではありません。それはただ鍵の存在です。したがって、あなたがやりたいことは次のようなものではありません。
if ($hash{$key} == 1) # then key is in the hash
代わりは:
if (exists $hash{$key}) # then key is in the set
exists
ハッシュの値を気にするよりも、チェックを実行する方が実際には効率的ですが、ここで重要なのは、ハッシュのキーだけでセットを表すという概念だけです。undef
また、ここで値として使用することにより、値を割り当てるよりも少ないストレージスペースを消費するという指摘もありました。(また、値は重要ではないため、混乱が少なくなります。私のソリューションでは、ハッシュの最初の要素にのみ値を割り当て、他の要素は残します。他undef
のいくつかのソリューションでは、側転を回して値の配列を作成します。ハッシュ;完全に無駄な努力)。
タイピングif ( exists $hash{ key } )
があまり面倒ではない場合(関心のある問題は、その値の真実性ではなく、実際にはキーの存在であるため、私はこれを使用することを好みます)、短くて甘いものを使用できます。
@hash{@key} = ();
ずっとそう思ってた
foreach my $item (@array) { $hash{$item} = 1 }
少なくとも素晴らしく、読みやすく、保守可能でした。
ここで前提となるのは、「配列に X が含まれているか?」という多くのことを行う最も効率的な方法であるということです。チェックは、配列をハッシュに変換することです。効率性は、多くの場合時間ですが、場合によってはスペース、場合によってはプログラマーの労力など、希少なリソースに依存します。リストとリストのハッシュを同時に保持することで、消費されるメモリが少なくとも2倍になります。さらに、テストや文書化などに必要なオリジナル コードをさらに作成しています。
別の方法として、List::MoreUtils モジュール、特に関数any()
、none()
、true()
および を見てfalse()
ください。map()
これらはすべて、 and と同様に、ブロックを条件として、リストを引数として取ります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 では、配列からハッシュへの方法は、この方法よりもほぼ 1100 倍高速に実行されますany()
(Ubuntu 6.06 のパッケージ化された Perl 5.8.7 では 1300 倍高速です)。
ただし、それだけではありません。配列からハッシュへの変換には約 0.04 秒かかります。この場合、配列からハッシュへの方法の時間効率がany()
メソッドよりも 1.5 倍から 2 倍速くなります。それでも良いですが、恒星ほどではありません。
私の直感ではany()
、ほとんどの場合、配列からハッシュへの方法が打ち負かされますが、より確かな指標 (多くのテスト ケース、まともな統計分析、おそらくいくつかの大きな- O 各メソッドのアルゴリズム分析など) ニーズによっては、List::MoreUtils の方が優れたソリューションになる場合があります。確かに柔軟性が高く、コーディングが少なくて済みます。覚えておいてください、時期尚早の最適化は罪です... :)
また、完全性についても注目に値します。これを2つの同じ長さの配列で行うための通常の方法で@keys
あり@vals
、ハッシュを使用することをお勧めします...
my %hash = map { $keys[$_] => $vals[$_] } (0..@keys-1);
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
Raldi のソリューションは次のようになります (元の '=>' は必要ありません)。
my %hash = map { $_,1 } @array;
この手法は、テキスト リストをハッシュに変換するためにも使用できます。
my %hash = map { $_,1 } split(",",$line)
さらに、次のような値の行がある場合: "foo=1,bar=2,baz=3" これを行うことができます:
my %hash = map { split("=",$_) } split(",",$line);
[含める編集]
提供される別のソリューション (2 行かかります) は次のとおりです。
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;
Perl6::Junctionを使用することもできます。
use Perl6::Junction qw'any';
my @arr = ( 1, 2, 3 );
if( any(@arr) == 1 ){ ... }
多くの集合論的操作を行う場合は、Set::Scalarまたは同様のモジュールを使用することもできます。次に$s = Set::Scalar->new( @array )
、 Set を構築します - そして、それをクエリすることができます: $s->contains($m)
.
#!/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'
};
名前空間を汚染したくない場合は、コードをサブルーチンに配置できます。
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;
また、順序付けられた連想配列を実装するTie::IxHashも確認してください。これにより、データの 1 つのコピーに対して両方のタイプのルックアップ (ハッシュとインデックス) を実行できます。