ハッシュ(3) 要素の操作

ハッシュ内の要素(キーと値のペア)を操作する関数を調べます。
まずは要素の削除から。ハッシュ内の特定の要素を削除するには delete 関数を使います。

# hash6.pl
my %tastes = (
	pepper => 'hot',
	cake   => 'sweet',
	lemon  => 'sour',
);
 
sub eat {
	for (sort keys %tastes) {
		print "Eating a $_ ... so $tastes{$_}!\n";
	}
	print "Bulp.\n";
}
 
eat();
delete $tastes{pepper};   # pepper を削除
eat();

delete $tastes{pepper}; でハッシュ %tastes から、キーが pepper である要素を削除しています。
eat は %tastes の食べ物を食べて感想を述べる(+ゲップする)関数です。(^_^;)
実行結果です。

Eating a cake ... so sweet!
Eating a lemon ... so sour!
Eating a pepper ... so hot!
Bulp.
Eating a cake ... so sweet!
Eating a lemon ... so sour!
Bulp.

pepper を削除した後の eat では pepper が表示されていない事がわかります。
ハッシュの要素が定義されているか調べるには関数 exists を使います。

# hash7.pl
my %fruits_basket = (
	apples  => 3,
	bananas => 5,
	oranges => 2,
);
 
print "The basket has: ";
for (sort keys %fruits_basket) {
	print "$fruits_basket{$_} $_, ";
}
print "\n";
 
print "Got apples? => ";
print exists $fruits_basket{apples} ? "Yes!" : "No.", "\n";
 
print "Got grapes? => ";
print exists $fruits_basket{grapes} ? "Yes!" : "No.", "\n";

exists で果物がバスケットに存在するかを尋ねています。三項演算子である A ? B : C は、A が真である時に B を、偽である時は C を返します。
実行結果です。

The basket has: 3 apples, 5 bananas, 2 oranges,
Got apples? => Yes!
Got grapes? => No.

気をつけるべきなのは、たとえ値が偽でもハッシュに存在すれば exists で真と評価されるという事です。
例えば、

# hash8.pl
my %fruits_basket = (apples => 3, bananas => 5, oranges => 2);
 
$fruits_basket{apples} = 0;
print "All apples are eaten.\n";
 
print "How many apples? => ";
print "$fruits_basket{apples} apples.\n";
print "Got apples? => ";
print exists $fruits_basket{apples} ? "Yes!" : "No.", "\n";

を実行すると、以下のように表示されます。

All apples are eaten.
How many apples? => 0 apples.
Got apples? => Yes!

リンゴは全て食べてしまって「0個」になっているにもかかわらず、「Got apples?(リンゴはありますか?)」の問いに「Yes!」と答えています。(^_^;)
この場合は、exists を使わずに $fruits_basket{apples} を直接評価するようにすれば大丈夫です。(もしくは delete してハッシュから削除する)

# hash8.pl
my %fruits_basket = (apples => 3, bananas => 5, oranges => 2);
 
$fruits_basket{apples} = 0;
print "All apples are eaten.\n";
 
print "How many apples? => ";
print "$fruits_basket{apples} apples.\n";
print "Got apples? => ";
print $fruits_basket{apples} ? "Yes!" : "No.", "\n";

実行結果です。

All apples are eaten.
How many apples? => 0 apples.
Got apples? => No.

今度はちゃんと答えてくれました。(^_^)

ハッシュ(2) ハッシュとループ

今度はハッシュの中にある値をそれぞれループで処理したいと思います。
最初は foreach $value (%hash) でできるかと思い、以下のように書いてみました。

# hash2.pl
my %capital_city = (
	Japan   => 'Tokyo',
	USA     => 'Washington',
	England => 'London',
);
 
foreach $capital (%capital_city) {
	print "$capital\n";
}

これを実行すると……

England
London
USA
Washington
Japan
Tokyo

と、キー、値、キー、値……の順番で出力される事がわかりました。でも、順番はバラバラですね。ハッシュの特徴として「順番は保存されない」ので、foreachなどで取り出しても順番は再現されません。
本やウェブで調べると、ハッシュのループには普通 keys 関数を使うようです。keys 関数は、引数として渡したハッシュの全てのキーを1つの配列にして返してくれます。ただし、順番はバラバラです。
そこで、アルファベット順に処理する為に sort 関数を使って、keys で得た配列をソートします。

# hash3.pl
my %capital_city = (
	Japan   => 'Tokyo',
	USA     => 'Washington',
	England => 'London',
);
 
for (sort keys %capital_city) {
	print "The capital of $_ is $capital_city{$_}.\n";
}

for と foreach は同じ意味でした。また、foreach で受け取る変数を省略すると自動的に $_ が使われるんでしたね。
実行結果です。

The capital of England is London.
The capital of Japan is Tokyo.
The capital of USA is Washington.

ちゃんとアルファベット順に出力されました。対応関係もバッチリです。
他にも、values 関数を使えば値の配列を取り出せます。

# hash4.pl
my %capital_city = (
	Japan   => 'Tokyo',
	USA     => 'Washington',
	England => 'London',
);
 
for (sort values %capital_city) { print "$_\n"; }

実行結果です。

London
Tokyo
Washington

値だけが表示されます。
他のループ方法もあります。each 関数を使えばハッシュからキーと値からなる2要素のリストを1つずつ得る事ができます。
each 関数を呼ぶ度にハッシュ内部のイテレータが1つ進むので、ハッシュ内の全要素を列挙できます。イテレータがハッシュの最後まで到達した時、each 関数は空リストを返し、イテレータを先頭に戻します。
ただし、each 関数が返すキーと値の順番もやはりバラバラなので注意が必要です。

# hash5.pl
my %languages = (
	Perl   => 'Larry Wall',
	Ruby   => 'Yukihiro Matsumoto',
	Python => 'Guido van Rossum',
);
 
while (($lang, $author) = each %capital_city) {
	print "The author of $lang is $author.\n";
}

while の条件部にeachで得たリストの代入文を書いています。代入文を評価する場合、代入後の左辺値が値として得られます。each 関数を呼び出していき、ハッシュの最後のペアまで到達すると、空リストが返りますが、空リストは偽(false)として評価されるので、そこで while ループが終了します。
実行してみます。

The author of Python is Guido van Rossum.
The author of Ruby is Yukihiro Matsumoto.
The author of Perl is Larry Wall.

順番はバラバラですね。(^_^)
each 関数を使ったループは、わざわざキーの配列などを作る必要がないのでコストが低いとの事。適材適所ですね。

ハッシュ(1) ハッシュの基礎

少し余裕がでてきたので、2歩下がってハッシュを勉強します。どうやらPerlとハッシュは切っても切れない縁があるようです。
元々ハッシュは連想配列(Associative array)と呼ばれていたのですが、タイプするのが面倒だという理由でハッシュ(Hash)と呼ぶようになったらしいです。「ハッシュ」という呼び方は、連想配列がハッシュ表で実装されているからとか。
ハッシュはリテラルは存在せず変数だけ。変数は % の後ろに変数名をつけると、ハッシュ変数として扱われます。
連想配列とも呼ばれる通り、ハッシュの機能は文字列「キー」に対応する「値」を格納する事。つまり「あれと言ったらこれ」というペア関係を記憶できます。「値」はスカラー値なら何でもいいのかな?
ハッシュに格納されている値を参照するには、ハッシュ変数の後ろに {キー} と付ければ、キーに対応する値を得る事ができます。左辺に置いて値を代入する事も可能です。配列の [数字] が {文字列} になった感じでしょうか。ちなみに、文字列はクォートする必要がありません。
また、キーで参照する場合、配列と同じように取り出した値はスカラー値なので「$hash{key}」のように、頭に「%」ではなく「$」を付けます。
ハッシュ変数に一括で値を代入する方法は

my %hash = ('apple', 'red', 'banana', 'yellow', 'melon', 'green');

のようにリストを使う方法(この場合「appleはred」「bananaはyellow」「melonはgreen」という対応関係が記録される)や、

my %hash = (apple => 'red', banana => 'yellow', melon => 'green');

と、シンタックスシュガーである「=>」を使う方法があります。("A", と A=> が等価なのでキーはクォートする必要がない)

my %hash;
$hash{apple}  = 'red';
$hash{banana} = 'yellow';
$hash{melon}  = 'green';

も同じ意味ですね。
動作を確認してみます。

# hash1.pl
 
my %fruits_color = (
	apple  => 'red',
	banana => 'yellow',
	melon  => 'green',
);
 
print "An apple is $fruits_color{apple}.\n";
print "A banana is $fruits_color{banana}.\n";
print "A melon is $fruits_color{melon}\n";

果物に対応する色を格納したハッシュ %fruits_color を用意して、それぞれの果物に対応する色を表示しています。
実行結果です。

An apple is red.
A banana is yellow.
A melon is green

上手くいきました。「=>」を使うとコードが読みやすくていいですね。