ハッシュ(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
上手くいきました。「=>」を使うとコードが読みやすくていいですね。