| Top Page | プログラミング | Perl 目次 | prev | next | 索引 |

7. 配列:データをまとめて管理する

配列というデータ構造

これまででてきた変数は,ひとつの変数がひとつの値を持っていました. こういう”ふつうの”変数をスカラー変数と呼びます. 変数の名前は自由につけられるので,いくらたくさんのデータがあっても, それぞれ別の名前をつけて処理することができます.

でも,たとえば 150 個体のマウスの体重をそれぞれ $weight001, $weight002,… $weight150 などという変数に記録しておくのは,どう見てもあまりかしこいやりかたでは ありません. たくさんのデータを効率良く管理するための仕組みがあってしかるべきです.

そんな仕組みのひとつが配列というデータ構造です. 配列は,”ふつうの”変数をたくさん順番にならべたものです. ならんだもの全体をひとつの名前で呼んで,一括して扱うことができます. このようなデータ構造を持った変数を配列変数と呼びます. 配列変数の名前は $ ではなくて @ で始まります.配列変数は,たとえばこんなふうにして 作ることができます(以下では,配列変数のことを簡単に配列と呼ぶこともあります).

@weight = (10.8, 11.2, 14.3);  #  3つの要素を持つ配列 @weight を作る.

配列に含まれるひとつの要素を参照したり,そこに値を代入したりするは, それが何番目の要素かを指定します.最初の要素は1番めではなくて ゼロ番めと数えます.

@weight = (10.8, 11.2, 14.3);  #  3つの要素を持つ配列 @weight を作る.

print $weight[0], "\n";  #   @weight の最初(0番め)の要素.10.8と表示.
$weight[2] = 12.1;       #   14.3 だった要素が 12.1 になる.
print $weight[2], "\n";  #   12.1 と表示される.

配列の要素をあらわすときには,@ ではなくて $ を最初につけます. また,何番目の要素かは,配列名のうしろの [] 内に書きます. これを添え字(そえじ)と呼びます. たとえば $weight[0] の 0 が添え字です.

配列全体のサイズは伸縮自在で,要素を付け加えたり取り除いたりできます. shift は配列の先頭の要素を取り去る命令で,取り除いた要素の値を返します. push は配列の最後に要素を追加する命令です.

@weight = (10.8, 11.2, 14.3);  #  3つの要素を持つ配列 @weight を作る.

$w = shift @weight;      #   $weight の先頭の要素を取り除く.取り除いた要素を $w に代入.
print $w, "\n";          #   10.8 と表示.
print $weight[0], "\n";  #   @weight の最初(0番め)の要素.11.2と表示.

push @weight, 9.7;       #   要素数が2つになっていた @weight の末尾に要素を追加.
print $weight[2], "\n";  #   9.7 と表示される.

配列の末尾のデータを取り除いたり(pop),配列の先頭にデータを 付け加えたり(unshift)もできます.

以下では,配列を活用するために知っておくとよいことを2つ説明します.


配列の代入

まず,配列の代入について説明します.代入演算子を使って配列を配列に代入すると, 左辺の配列は右辺の配列と同じになります(同じ要素数で,各要素の値も同じ). 代入演算子の右側には,配列でなくて,() のなかに任意個の変数やリテラルを並べた リストを書くこともできます.上の例で,最初に3つの要素を持つ配列変数 @weight を つくるところは,配列へのリストの代入というかたちをしています.

ふつうの(スカラー)変数に配列変数を代入しようとすると,配列の要素数が左辺の変数に 代入されます.これで,配列の要素数を簡単に知ることができます.

@weight = (10.8, 11.2, 14.3);  #  3つの要素を持つ配列 @weight を作る.

$num = @weight;       #  スカラー変数 = 配列変数; と書くと,配列の要素数が代入される.
print $num, "\n";     #  3 と表示.
shift @weight;        #  配列の先頭要素を取り除く.
$num = @weight;       #  
print $num, "\n";     #  2 と表示.

このしくみはコンテキストという概念と関係しているのですが,コンテキスト の説明はまたあとで.


配列と繰り返し構文を組み合わせて使う

配列は,よく繰り返し構文と組み合わせて使われます. たくさんの(しばしば前もって何個あるか分からない)データを順次処理するには, この組み合わせが最適です. たとえば,for 文と組み合わせて,配列の全要素を参照することができます.

@weight = (10.8, 11.2, 14.3);  #  3つの要素を持つ配列 @weight を作る.

$num = @weight;       #  スカラー変数 = 配列; と書くと,配列の要素数が代入される.

for ($i = 0; $i < $num; ++$i) {  #  配列の要素数だけ繰り返す.
    print $weight[$i], "\n";        #  0番めから2番めまで,順次表示.
}

for に似た繰り返し構文として,foreach 文があります.これはかならず配列ないしは リストと組み合わせて使います.

foreach スカラー変数 (配列・リスト) {
    処理
}

こんな構造の文です. スカラー変数は() 内の配列ないしはリストの各要素の「別名」になります. スカラー変数が $x という名前,() 内には @array という配列変数が書かれている としましょう(arrayは配列の英語です). {} 内のブロックが最初に実行されるときは,$x は @array の1つめの要素の別名, 2回めにブロックが実行されるときは $x は @array の2つめの要素の別名, というぐあいに @array の要素数だけ繰り返されます.

こんな説明だけでは分かりにくいので,実例をあげます.

@weight = (10.8, 11.2, 14.3);  #  3つの要素を持つ配列 @weight を作る.

foreach $w (@weight) {  #  $w は,配列 @weight の各要素の別名.
    print $w, "\n";     # 
}

繰り返しの最初のときは, $w は @weight の最初の要素の別名なので,print $w と書けば 10.8 が表示されます.2回めのときは,$w は配列の次の要素 11.2 の別名になるので, print $w で 11.2 が表示されます.

$w は @weight の各要素の別名になるのであって, 各要素が $w に「代入」されるのではありません. それは,こんなプログラムを実行してみれば確かめられます.

@weight = (10.8, 11.2, 14.3);  #  3つの要素を持つ配列変数 @weight を作る.

foreach $w (@weight) {    #  $w は,配列 @weight の各要素の別名.
    $w += 10;             #  $w が 10 増える → @weight の要素の値が 10 増える.
}

for ($i = 0; $i < 3; ++$i) {
    print $weight[$i], "\n";     #  20.8, 21.2, 24.3 が表示される.
}

for 文を使って値を表示しているところは,もちろん foreach 文で 書くこともできます.

foreach 文も for 構文も配列の全要素を処理するのに使えますが, foreach 文では添え字で要素を指定しないので,あらかじめ 配列の要素の数を調べなくてもよいという違いがあります.


配列を使ってみる

データファイル中のデータを読み込んで配列に記憶させてみます. split の使い方を説明したページ 中の例をもういちど使います.

Atsushi 177.0   75.0
Hide    175.0   68.0
Yutaka  180.0   78.0
Koji    182.0   74.0
Shinji  175.0   74.0
Mitsuo  173.0   68.0

各行に,名前,身長,体重が並んだデータファイルです. わざとらしい例ですが,このデータを読み込んで,いったん配列にしまってから 順次表示するプログラムを書いてみます.

while ($line = <>)  {
    chomp $line;  #  $line の末尾の改行コードを削除する.

    # 空白文字で分割して作ったリストを $name, $height, $weightに代入.
    ($name, $height, $weight) = split (/\s+/, $line);  

    push @name_array, $name;      # 配列 @name_array の末尾に名前データを追加.
    push @height_array, $height;  #  以下,同様.
    push @weight_array, $weight;
}

$num_data = @name_array;    #  配列をスカラー変数に代入すると配列の要素数が代入される.

for ($i = 0; $i < $num_data; ++$i) {  # 配列の要素数だけ繰り返す.
    # Body Mass Index (体重/身長の二乗)を計算して,配列にしまう.
    $bmi = $weight_array[$i] / ($height_array[$i] / 100.0) ** 2.0;
    push @bmi_array, $bmi;
}

for ($i = 0; $i < $num_data; ++$i) {  #  配列の要素数だけ繰り返す.
    printf "%s\t%.1f\n", $name_array[$i], $bmi_array[$i]; 
    # 全員の名前と BMI (小数点以下1ケタまで)を表示する
}

コメントを参考にしながら読んでください. すでに説明したことだけを使って書いています. ひとつ注意するべきことは,ふつうの(スカラー)変数と同様, これまで存在していない配列変数がプログラム中に登場したら新規に 作られるということです.新規に作られた配列変数は(当然ですが)要素が ひとつもない空の配列です.

上のプログラムの push @name_array, $name; という文が最初に実行される ときは,@name_array という配列変数は存在していないので,あらたに空の配列 @name_array が作られて,そこに $name の値が最初の要素として加えられます.

あとで見たときに分かりやすいプログラムにするために, データを読み込み始める前に

@name_array = ();
@height_array = ();
@weight_array = ();
@bmi_array = ();

のように書いておき,こういう名前の配列変数を作るんだよということを 明示するのもよいでしょう.

なお,上の例を「わざとらしい」と書いたのは,一行のデータを読むたびに BMI を計算して印刷しても同じ出力が得られのに,わざわざ身長や体重をいったん 配列にしまってから処理しているからです. 配列は,たくさんのデータを_同時に_覚えておいて作業をする必要がある場合に 活躍するデータ構造です. 配列を活用する例を次のページで紹介します.


書き方のおさらい

次のページに進む前に,変数名の書き方を整理しておきます.

です.

スカラー変数と配列の名前は独立に管理されていて, $x と @x はたがいに無関係です. したがって, $x と $x[0]もたがいに無関係です.



| Top Page | プログラミング | Perl 目次 | prev | next |