Perl プログラミング - HASH

PROGRAM


HASH を使って変数に名前をつける。

目次

  • HASH とは … 概要を捕らえるためのお話です。
  • HASH の基本構文 … 使う上で知っておかなくてはいけないお話です。
  • HASH を編集する … 格納された値やキー自身の追加や削除のお話です。
  • 関数とのやり取り … HASH を関数とやり取りする上での注意事項です。
  • 値を一括で操作する … map を使った一括操作の一例です。
  • リファレンスとして使用する … リファレンスとそれに絡んで匿名 HASH のお話です。

 

HASH とは

HASH とは、連想配列とも言って、値に対して名前をつける事ができる変数の型です。

 

変数の名前というと、今までも $label というように名前をつけてきましたけど、それとはまた違って、もっとずっと便利になります。

たとえば、英和辞書を考えて見ます。

英単語 意味
pretest (精密検査前の) 予備検査
November 11月
swamp 湿地, 沼地

辞書の中には英単語が並んでいて、その日本語の意味が引けるようにできていますよね。ここでいう英単語が 「名前」 にあたり、意味が 「値」 にあたるような感じです。

 

これを Perl で管理しようとしたとします。

いままでの方法だと…、スカラー値 ( $ ) を使うのが妥当…でしょうか。これらの単語をたとえば次のようにプログラミングすることになると思います。

$pretest = '(精密検査前の) 予備検査';

$November = '11月';

$swamp = '湿地, 沼地';

さて、単語が増えたときの追加自体は、同様にすればいいので簡単…と。

けれど、もし英単語をすべて表示するプログラムが組まれている場合はどうでしょう。上記の例の場合、英単語をすべて表示する場合は、次のように組むのがシンプルです。

print "pretest: $pretest\n";

print "November: $November\n";

print "swamp: $swamp\n";

単語が増えるたびにこちらにも追加する必要が出てきます。面倒ですが簡単です。いや、簡単ですがとても面倒です。

しかも単語を追加したり削除したりするたびに、書き換えなくてはいけません。このように1箇所 (実際には登録と処理の2箇所) だけなら把握できるでしょうけど、これが 3 つ、 4 つと増えていくととても人間の手には負えなくなってしまいます。

こういう方法は直す箇所を 1 箇所忘れただけで、とたんにプログラムの不具合につながりますから、保守の面でも絶対にやるべきではない事だと自分では思います。

他にも、「では、指定した意味を持っている単語はどれだろう…」 と探すプログラムを組もうとしたときに…、はたしてどう作ったらいいのでしょう…。できないことはないでしょうけど、少なくとも自分にはそれを工夫する根気もなければ、作り上げる自信もないのでした。

 

そんな問題を、いっきに、しかもあっさりと解決してくれるのが HASH です。値に名前を持たせるだけでプログラムの幅が驚くほど広がるのです。なお、正式には、値につけた名前のことを "キー" と呼びます。

 

HASH の基本構文

HASH を使用する場合、変数名の前に % をつけます。

たとえば hash という名前の HASH を作成したい場合は、次のような感じになります。下の例では局所変数であることを示す my をつけて宣言しています。

my %hash;

HASH につけられた名前(キー) が所持している値は、たとえば %hash の中から "name" という名前の付けられた値を操作する場合は次のように記述します。

# 頭につける記号が、スカラーを表す $ になります。

$hash{'name'}

注意すべき点は、変数名の頭につける記号が、% ではなく値を示す $ になることと、名前(キー) を変数名の後に中括弧でくくって指定するといったところでしょうか。

 

HASH はもちろん、値の代入も取得もできます。やり方は普通のスカラー操作と同様です。

# 値を取得する場合

$data = $hash{'name'};

 

# 値を代入する場合

$hash{'name'} = $data;

ただ、少しだけ HASH 特有のものがあるので、それを下に少し挙げておきます。

  • 同じ名前(キー) に値を代入すると、以前の値が上書きされる。
  • 存在しない名前(キー) に値を代入すると、新しくそのキーが登録されます。
  • 存在しない名前(キー) の値を取得してもエラーになりません。

自由すぎてときどき把握できなくなりますけど、多くの場合、これらの特徴のおかげで非常にプログラムが組みやすくなります。

 

HASH は、上記のように値を追加するほかに、一括して値を設定 (初期化) する方法が用意されています。

たとえば冒頭の英単語を一括して %dictionary という HASH に登録したい場合は次のようにして登録する事が可能です。一応、少し上で述べた方法と合わせて、2通りの方法を書いておきます。

# 1つずつ登録する場合

$dictionary{'pretest'} = '(精密検査前の) 予備検査';

$dictionary{'November'} = '11月';

$dictionary{'swamp'} = '湿地, 沼地';

 

# 一括して初期化する場合は、%hash = ('key' => 'value', ...) という形で書けます。

%dictionary = ('pretest' => '(精密検査前の) 予備検査', 'November' => '11月', 'swamp' => '湿地, 沼地');

このように配列と同じような感じで、初期値を括弧でくくって指定することが可能です。

違う点としては、代入先の変数は HASH なので当然 % 記号が頭につけられていることと、登録する要素として、”名前 => 値” という形で指定されているところでしょうか。

純粋に ”名前, 値, 名前, 値" と、交互にカンマ区切りで指定することもできるみたいですけど、HASH らしくないので個人的にはあまりお勧めしません。

なお、上記の一括初期化、人それぞれ好みがありますけど、次のようにするとなんだかすっきり見やすくなると思います。

# 自由に改行を入れられることを利用して…

%dictionary =

(

'pretest' => '(精密検査前の) 予備検査',

'November' => '11月',

'swamp' => '湿地, 沼地'

);

参考にどうぞです。

 

関数とのやり取り

HASH も、関数間で簡単に受け渡しをすることができます。受け渡し方も、スカラー ( $ ) や配列 ( @ ) などのときと同じように、ごく普通に引数や戻り値として利用する事が可能です。

HASH を関数に渡して結果を受け取るプログラムとその HASH を受け取って結果を返す関数の例を挙げると、次のような感じになります。大雑把な枠組みだけで、具体的な処理は入っていません。

# HASH を受け取って、HASH を返す関数

sub func(%)

{

    # 受け取るときは、@_ をそのまま % に代入すれば OK です。

my %hash = @_;

 

# 返すときも、% をそのまま return に指定すれば OK です。

return %hash;

}

 

# HASH を渡して戻り値を受け取る

my %give;

my %take;

 

# 関数に渡すときも関数から受け取るときも、普通に % でやりとりすることができます。

%take = &func(%give);

特に難しいこともなく、普通に操作することができます。ただ、関数内で受け取るときに @_ をそのまま % に代入しているところに少し違和感を感じるかもしれないので、そういうものだと思いつつ、気にしておきましょう。

 

値を一括で操作する

たとえば、HASH に保存された値すべてで、小文字のアルファベットをすべて大文字にする場合はどうしよう、というお話です。HASH においても、配列のときのように、保持している要素を繰り返し処理する方法が備わっています。

 

その上で大切になるのが keys という関数です。

この関数は HASH に保存されているキー (名前) の一覧を配列で取得する関数です。これを用いることで、その HASH がどういったキーを持っているか把握することができます。

# @key_list に %hash が保持しているキーの一覧を取得します。

# keys は組み込み関数なので、引数を括弧でくくる必要はありません。

 

@key_list = keys %hash;

 

キーの一覧を配列で取得したら、あとは配列と同じ要領で HASH を操作する事ができます。 たとえば、アルファベットを大文字に変換するプログラムを組むとしたら、次のような感じになります。

# foreach を使って %hash のキーそれぞれについて処理を行います。$_ には keys %hash で取得したキー一覧の配列が、ひとつずつ取り出されます。

# 本来 $_ という変数名は省略時の変数名なので書かないのですけど、今回はあえて明示してみました。

 

foreach $_ (keys %hash)

{

# とりだしたキーに対して、小文字を大文字に変換する処理を行います。

# 変換処理は「正規表現」を使って行っています。なぜ "=~ tr/[a-z]/[A-Z]/" で変換できてしまうのかは、とりあえず気にしないでください。

 

$hash{$_} =~ tr/[a-z]/[A-Z]/;

}

これで %hash のすべての要素が、大文字に変換されるのでした。

または map 関数を使って行うこともできます。これはすこしややこしくなるので参考程度に眺めてくださって大丈夫です。基本的には foreach を少し変化させた感じですので、比べながら見るとわかりやすいかもしれません。

# map の場合、2 番目の引数で指定した配列の値は自動的に $_ に取得されます。

map ( $hash{$_} =~ tr/[a-z]/[A-Z]/, keys %hash );

結果はまったく同じなので Perl らしい構文になれていて、かつ、すっきりと1行で収めたい場合はこちらをどうぞ。

 


[ もどる ]