配列があり、「配列にはXが含まれていますか?」をたくさん実行することがわかっているとします。チェックします。これを行う効率的な方法は、その配列をハッシュに変換することです。ここで、キーは配列の要素であり、次のように言うことができます。
if($ hash {X}){...}
この配列からハッシュへの変換を行う簡単な方法はありますか?理想的には、匿名配列を取得して匿名ハッシュを返すのに十分な汎用性が必要です。
回答:
@hash{@array} = (1) x @array;
これはハッシュスライスであり、ハッシュからの値のリストであるため、リスト-y @を前に取得します。
ドキュメントから:
ハッシュスライスで「%」の代わりに「@」を使用する理由について混乱している場合は、次のように考えてください。角かっこのタイプ(正方形または中括弧)は、それが配列であるかハッシュであるかを決定します。一方、配列またはハッシュの先頭の記号( '$'または '@')は、単数形(スカラー)または複数形(リスト)のどちらを返すかを示します。
@hash{@keys} = undef;
でハッシュを参照しているここでの構文@
は、ハッシュスライスです。基本的に、$hash{$keys[0]}
AND $hash{$keys[1]}
AND $hash{$keys[2]}
...は左辺値の=の左側にあるリストであり、そのリストに割り当てます。このリストは実際にハッシュに入り、すべての名前付きキーの値を設定します。この場合、1つの値のみを指定したため、値はに$hash{$keys[0]}
なり、他のハッシュエントリはすべて、未定義の値で自動的に有効になります(生き返ります)。[ここでの私の最初の提案は、式= 1に設定されていました。これにより、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
いくつかのソリューションでは、カートホイールを回して値の配列を作成します。ハッシュ;完全に無駄な努力)。
= ()
、ではなく= undef
、最初の値の後だけでなく、すべての値に暗黙的にundefを使用する際の一貫性のためだけです。(これらのコメントに示されているように、を見てundef
、1に変更して、すべてのハッシュ値に影響を与えることができると考えるのは簡単すぎます。)
タイピングif ( exists $hash{ key } )
があまり手間がかからない場合(関心のある問題は、その値の真実性ではなく、実際にはキーの存在であるため、私が使用することを好む)、短くて甘いものを使用できることに注意してください
@hash{@key} = ();
ここには、多くの「配列にXが含まれていますか?」を実行するための最も効率的な方法であるという前提があります。チェックは、配列をハッシュに変換することです。効率は、リソースの不足、多くの場合時間、場合によってはスペース、場合によってはプログラマーの努力に依存します。リストとリストのハッシュを同時に保持することで、消費されるメモリを少なくとも2倍にします。さらに、テストや文書化などに必要な、よりオリジナルのコードを書いています。
代替、リストを見て:: 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の関数。
ソースから構築されたPerl5.8.8では、配列からハッシュへのメソッドは、 any()
メソッド(Ubuntu6.06のパッケージ化されたPerl5.8.7では1300倍高速です)。
ただし、これは完全な話ではありません。配列からハッシュへの変換には約0.04秒かかります。この場合、配列からハッシュへの方法の時間効率は1.5倍から2倍速くなります。 any()
メソッドのます。それでも良いですが、それほど恒星ではありません。
私の直感ではany()
、ほとんどの場合、配列からハッシュへの方法が勝つだろうと思いますが、もっとしっかりしたメトリック(たくさんのテストケース、まともな統計分析、おそらくいくつかの大きなもの)があれば、はるかに気分が良くなります。 O各メソッドのアルゴリズム分析など)ニーズによっては、List :: MoreUtilsの方が適している場合があります。それは確かにより柔軟で、より少ないコーディングを必要とします。時期尚早の最適化は罪であることを忘れないでください... :)
List::MoreUtils
ユースケースに応じて、適切な方法である場合とそうでない場合があります。ユースケースには多くのルックアップがある場合があります。他の人はそうではないかもしれません。重要なのは、配列からハッシュへの変換とList::MoreUtils
、メンバーシップを決定するという根本的な問題の解決の両方です。複数のアプローチを知っていると、特定のユースケースに最適な方法を選択できます。
また、完全性のために注目に値するのは、2つの同じ長さの配列@keys
で@vals
これを行うための私の通常の方法であり、ハッシュでした...
my %hash = map { $keys[$_] => $vals[$_] } (0..@keys-1);
@keys-1
です$#keys
。
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 )
、セットを作成します-そして、次のコマンドでクエリを実行できます$s->contains($m)
。
名前空間を汚染したくない場合は、コードをサブルーチンに配置できます。
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
#!/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'
};
順序付けられた連想配列を実装するTie :: IxHashも確認することをお勧めします。これにより、データの1つのコピーに対して両方のタイプのルックアップ(ハッシュとインデックス)を実行できます。