次のプログラムでハッシュを使用して発生をカウントした後、実際のリストの順序を維持するにはどうすればよいですか?例えば、 <DATA>
は
a
b
e
a
c
d
a
c
d
b
etc.
ハッシュを使用して、各要素の出現をカウントしました。
そして私が欲しいのは:
a 3
b 2
e 1
c 2
d 2
しかし、次のプログラムはそれ以外のことを私に示しています。
my (%count, $line, @array_1, @array_2);
while ($line = <DATA>) {
$count{$line}++ if ( $line =~ /\S/ );
}
@array_1 = keys(%count);
@array_2 = values(%count);
for(my $i=0; $i<$#array_1; $i++)
{
print "$array_1[$i]\t $array_2[$i]";
}
ハッシュは注文されていませんが、いつものように、CPANは解決策を提供します: Tie :: IxHash
use Tie::IxHash;
my %count;
tie %count, 'Tie::IxHash';
while ($line = <DATA>) {
$count{$line}++ if ( $line =~ /\S/ );
}
while( my( $key, $value)= each %count) {
print "$key\t $value";
}
ハッシュテーブルのデータは、キーのハッシュコードの順序で格納されます。これは、ほとんどの場合、ランダムな順序のようなものです。また、各キーの最初の出現順序を保存する必要があります。この問題に取り組む1つの方法は次のとおりです。
my (%count, $line, @display_order);
while ($line = <DATA>) {
chomp $line; # strip the \n off the end of $line
if ($line =~ /\S/) {
if ($count{$line}++ == 0) {
# this is the first time we have seen the key "$line"
Push @display_order, $line;
}
}
}
# now @display_order holds the keys of %count, in the order of first appearance
foreach my $key (@display_order)
{
print "$key\t $count{$key}\n";
}
perlfaq4 の答えから "ハッシュに要素を入れた順序を記憶させるにはどうすればよいですか?"
要素をハッシュに配置した順序をハッシュに記憶させるにはどうすればよいですか?
CPANのTie :: IxHashを使用します。
use Tie::IxHash;
tie my %myhash, 'Tie::IxHash';
for (my $i=0; $i<20; $i++) {
$myhash{$i} = 2*$i;
}
my @keys = keys %myhash;
# @keys = (0,1,2,3,...)
単に:
my (%count, @order);
while(<DATA>) {
chomp;
Push @order, $_ unless $count{$_}++;
}
print "$_ $count{$_}\n" for @order;
__DATA__
a
b
e
a
c
d
a
c
d
b
またはワンライナーとして
Perl -nlE'$c{$_}++or$o[@o]=$_}{say"$_ $c{$_}"for@o'<<<$'a\nb\ne\na\nc\nd\na\nc\nd\nb'
もう1つのオプションは、David Golden(@xdg)の単純な純粋なPerl Hash::Ordered
モジュール。順序は決まりますが、ハッシュがバックグラウンドでオブジェクトになり、ハッシュ要素にアクセスして変更するためのメソッドを使用するため、処理が遅くなります。
モジュールが通常のハッシュよりもどれだけ遅いかを定量化できるベンチマークはおそらくありますが、小さなスクリプトでキー/値データ構造を操作するためのクールな方法であり、その種のアプリケーションでは十分に高速です。ドキュメントには、ハッシュを注文するための他のいくつかのアプローチも記載されています。
これが常に優れたテクニックであるとは思いませんが、時々使用しました。 「見た」タイプのハッシュを保持するだけでなく、通知されたカウントと順序の両方を格納できます。
基本的に、$count{$line}
が表示された回数ではなく、$count{$line}{count}
が表示された回数であり、$count{$line}{order}
が表示された順序です。
my %count;
while (my $line = <DATA>) {
chomp $line;
if ($line =~ /\S/) {
$count{$line} ||= { order => scalar(keys(%count)) };
$count{$line}{count}++;
}
}
for my $line (sort { $count{$a}{order} <=> $count{$b}{order} } keys %count ) {
print "$line $count{$line}{count}\n";
}
ハッシュは、Perlで割り当てられるまでは単なる配列であるため、配列としてキャストすると、元の順序で繰り返すことができます。
my @array = ( z => 6,
a => 8,
b => 4 );
for (my $i=0; $ar[$i]; ++$i) {
next if $i % 2;
my $key = $ar[$i];
my $val = $ar[$i+1];
say "$key: $val"; # in original order
}
明らかにそうすると、ハッシュインデックスの利点が失われます。ただし、ハッシュは単なる配列であるため、配列をハッシュに割り当てるだけでハッシュを作成できます。
my %hash = @array;
say $hash{z};
これは「配列をインデックスとして使用する」ソリューションのバリエーションにすぎないかもしれませんが、インデックスを手動で(または他の方法で)入力する代わりに、ソース配列から直接作成しているため、より適切だと思います。