第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になるのかな?
values関数
ハッシュ内の値のみからなるリストを取り出すvalues関数があるらしい。
print "values of \%hash = (", join(', ', values %hash), ")\n";
keysはキーのリスト、valuesは値のリストを取得するための関数。C#でもIDictionary
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"; }