第5章「ハッシュ」

辞書の実装として用いられるハッシュをプリミティブなデータ構造として持っているのね。ミ・д・ミ ホッシュホッシュ

ハッシュの作り方

my %map = (
    'foo' => 42,
    'bar' => 23,
    'baz' => 3.14,
);

値をカンマで区切るとリストになるという事は始めの方で知ったけど、

('foo' => 42, 'bar' => 23, 'baz' => 3.14);

というのは、リストなのかな?'foo' => 42 というのはハッシュのエントリとなるオブジェクトを作るための演算なのかな?んで、そのオブジェクトがリストの要素になってるのかな?
どんなオブジェクトになるのか確かめるべく

my $hoge = ('foo' => 42);
print $hoge, "\n";

ってしたけど怒られた。ぐぬぬ

ハッシュ内の値へアクセス

print "foo -> $map{'foo'}";

配列のときはbracketsで添字を囲むけど、ハッシュの場合はbraceになるのね。変数名の先頭は配列と同じく$になる。

キーの取得

ハッシュのキーのみからなるリストはkeys関数で取得できる。よって、次のようにするとハッシュ内の要素を全て走査できる。

foreach (keys(%map))
{
  print "$_ -> $map{$_}\n";
}

ハッシュへの新しいエントリの追加

$map{'qux'} = 'hoge';

キーは重複してはダメなので、既に存在するキーであれば、値が上書きされてしまう。

$map{'baz'} = '1';

すでに'baz'なるキーが存在する状況で上記を実行すると、その値が1に書き換えられてしまう。

キーでソートして走査

ハッシュに格納されているエントリの順序には意味が無いけど、表示するときにソートしたい場合などがあるかもしれない。そういうときは以下のようにkeysで取り出すキーにsortをかましてやればキーの昇順で取り出せる。

foreach (sort keys %map)
{
  print "$_ -> $map{$_}\n";
}

print関数の時も思ってたんだけど、Perlって関数呼び出すときにカッコとコッカで囲まなくてもいいっぽい?上の

sort keys %map

sort(keys(%map))

のことだもんなぁ。

値でソートして走査

配列のsortで数値として比較しつつソートする方法を学んだけど、それを応用するとハッシュを値でソートして走査することもできる。

my %hash = ('one' => 1, 'two'=>2, 'three'=>3, 'four'=>4);
foreach (sort { $hash{$a} <=> $hash{$b} } keys %hash)
{
  print "$_ -> $hash{$_}\n";
}

比較の部分の$aと$bを逆にすると降順になる。

foreach (sort { $hash{$b} <=> $hash{$a} } keys %hash)
{
  print "$_ -> $hash{$_}\n";
}

やっぱ

{ $hash{$a} <=> $hash{$b} }

の部分が謎。こいつは一体なんなんだー。

存在しない値を++でインクリメント

ハッシュの実例として、123ページから124ページにかけて、次のようなスクリプトが紹介されてた。

my %counter;
my @names = ('Yuki', 'Tomura', 'Sato', 'Sato', 'yuki', 'Momoru', 'Tomura', 'Tomura');
foreach my $name (@names)
{
  $counter{$name}++;
}
foreach my $name (sort keys %counter)
{
  print "$name $counter{$name}\n";
}

このスクリプト

$counter{$name}++;

の部分で、初めて出現した$nameに必ず出くわすはずだ。まだ登録されていない$nameをキーに値を取り出すとどんな値が取り出せるのかはよく分からないけど、少なくとも0と見なせる値が取り出せて、なおかつ++できるようだ。なんか不思議。これも「コンテキスト」なんだろうな。
ということで、どんな値が取り出せるのか確認するために、++する直前に

print "$name -> $counter{$name}\n";

を追加して実行すると、

Use of uninitialized value in concatenation (.) or string

なるエラーが出た。つまりこいつもundefなのかな?という事は、undefをスカラーコンテキストで評価すると0になるのかな?

環境変数%ENV

環境変数はハッシュ%ENVに格納されている。

foreach (keys %ENV)
{
  print "$_ -> $ENV{$_}\n";
}

とかすると全部ながめられる。

values関数

ハッシュ内の値のみからなるリストを取り出すvalues関数があるらしい。

print "values of \%hash = (", join(', ', values %hash), ")\n";

keysはキーのリスト、valuesは値のリストを取得するための関数。C#でもIDictionaryインターフェースにKeysとValuesというプロパティがあるね。ちなみにDictionaryクラスもハッシュで実装されていて、SortedDictionaryクラスの方は平衡木(赤黒木)で実装されている。

each関数

ハッシュにたくさんエントリが登録されていると、keys関数をかませてキーのリストを取り出すとかなりメモリを食うらしい。そんなときは代わりにeach関数をかますといい。ただし、each関数が返す値はキーと値からなるリスト。

while (my($key, $val) = each(%hash))
{
  print "$key -> $val\n";
}

みたいな感じで使える。
これまで本を読んできて、リストはC#で言うところのIEnumerableのようなもんだと思ってた。無限に長い列も扱えますよ、みたいな。でもそうじゃなさげ?

exits関数とdelete関数

ハッシュ%hash内に、$keyで登録されたエントリが存在するかどうかを確かめるにはexists関数を、キー$keyで登録されているエントリを削除するにはdelete関数を使う。http://ideone.com/s3pHN

my %hash = ('foo'=>42, 'bar'=>23, 'baz'=>3.14);

print "exists\n";
if (exists($hash{'qux'}))
{
  print "exists\n";
}
else
{
  print "not exists\n";
}
print "\n";

print "delete\n";
delete($hash{'foo'});
foreach (keys %hash)
{
  print "$_ -> $hash{$_}\n";
}