2部 Perl言語仕様

配列

1.リスト

複数のスカラ変数を並べた構造を『リスト』といいます。リストはスカラと同じように数値、文字列を区別なく扱います。リストの書き方はとても簡単で、値をカンマで区切り、その全体をカッコで囲むだけです。1、2、3の 3 つの値のリストは、以下のように表します。

(1, 2, 3)

リストの代入

リストの要素に代入する場合は、下記のような方法があります。

($a, $b, $c) = (1, 2, 3);

リストの中にリストがある場合には、自動的に展開されます。

(@list1, @list2, &function)

上記のようなリストは @list1 のすべての要素の後に @list2 のすべての要素を追加し、その後にfunction というサブルーティンが返すすべての要素を追加します。

リストの要素数と変数の数が違う場合

リストの要素数よりも、用意した変数のほうが多い場合には、あまった値は捨てられます。

($a, $b, $c) = (1, 2, 3, 4, 5);

上記の場合、リストの最初の3つの要素1、2、3がそれぞれ$a、$b、$cに代入され、残りの要素4、5は捨てられます。

逆に、リストの要素数よりも変数のほうが少ない場合、あまった変数には未定義値が代入されます。

($a, $b, $c) = (1, 2);

$aには1、$bには2、$cには未定義値の undef が代入されます。

リストと配列

一般的にリストが使われるのは、『配列』に対して値を設定するときです。リストのうち、変数名をつけたものを配列と呼びます。配列を宣言するときは変数名の前にアットマーク( @ )を付けるのが決まりになっています。

 @list = (1, 2, 3);

上記の例では、配列 @list は、1、2、3という3つの値を持つようになります。

2.配列へ値を代入

スカラ変数の場合は1つの変数に対して1つの値を代入するだけでしたが、配列の場合は複数の値を代入することができるため、スカラ変数とは違う代入の書式があります。

配列の初期化

空の配列の作成は次のようにします。値の入った配列を初期化するのにも使えます。

@week = ( );

配列の宣言と同時に値を代入するには、要素全体をカッコで囲み、各要素をカンマで区切ります。

@week = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');

インデックスを指定して要素に値を代入

配列はスカラ変数の要素ごとに『インデックス』を持っています。インデックスは個々の要素にアクセスするときに使います。たとえば$week[0]は、配列の1番目の要素を表します。インデックスの使用の際に注意するのは、2点です。まず、配列の最初の要素のインデックスは0 になります。1番目の要素のインデックスは 0 、2番目の要素は 1 になります。次に、配列の要素はスカラ変数ですから、変数名の前につくマークはアットマーク( @ )ではなくダラー( $ )になります。

$week[0] = 'Sun';
$week[1] = 'Mon';

qw演算子を使って初期化することもできます。qw 演算子はスペース( )で区切った単語を配列として返します。

@week = qw(Sun Mon Tue Wed Thu Fri Sat);

要素の範囲を指定して値を代入することもできます。

# 0 から 3 までの要素に値を代入
$week[0..3] = ('Sun','Mon','Tue','Wed');

値を捨てる

localtime関数のように沢山の値が帰ってくる場合、必要な値だけを変数に渡す方法がいくつかあります。

# こんなにいらないし、メモリの無駄使い
($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst)
= localtime( $time );
# スライスでほしい値を選択するが一番スマートな方法
($mday, $mon, $year)
= (localtime( $time ))[3,4,5];
# undef で値を捨てるのもあり
(undef undef, undef, $mday, $mon, $year, undef, undef, undef)
= localtime( $time );

3.配列の値を参照

配列への値代入と同様に、値を参照する場合も配列独自の書式があります。

インデックスを使って配列の値を参照

配列の要素を参照するには、$week[2]のように、インデックスを[ ] で囲んで指定します。

@week = (Sun, Mon, Tue, Wed, Thu, Fri, Sat);
# 配列 @week の3番目の要素を表示
print "$week[2]\n";
> Tue

配列のインデックスは0から始まるので、$week[2] は3つめの要素「Thu」を参照します。

配列を展開する

配列の要素をすべて表示する場合は下記のとおりです。

# 全ての配列の要素を表示
print "@week";
> Sun Mon Tue Wed Thu Fri Sat

上記の例では、配列をダブルクォーテーション( " )で囲み、変数展開をしています。スカラ変数の場合は単一の値が表示されるわけですが、配列の場合はすべての値がスペース()で区切られて表示されます。

配列を出力するときの区切り文字は特殊変数 $" で変更できます。

$" = ',';
print "@week";
> Sun,Mon,Tue,Wed,Thu,Fri,Sat

配列をダブルクォーテーション( " )で囲まない場合は、展開が行われないので、配列の各要素の値は区切りもいなしで表示されます。

# 全ての配列の要素を表示
print @week;
> SunMonTueWedThuFriSat

配列の要素数を調べる

配列の要素数は配列をスカラに代入することで得られます。

$length = @week;     # $length の値は 7

配列の要素数を調べ、要素がなければエラーにする場合は以下のようにします。

if ( $#list < 0 ){
	die("配列が空です!");
}

最後のインデックスの数値を調べる

「$#配列名」という表記は、配列の最後のインデックスの数値を表します。配列のインデックスは0から始まるので、配列の要素よりも1つ小さい値になります。

# 配列の最後にあたるインデックスを表示
print "$#week\n";
> 6

「$#配列名」 に代入することにより、配列の要素数を小さくしたり、空にしてしまうこともできます。

# 配列の初期化
@list = (1,2,3);
#配列の最後のインデックスを3にする
$#list = 2;

4.配列の使い方

for 文を使って配列にアクセス

配列のすべての要素にアクセスするには for文や foreach文が便利です。

@list = (One, Tow, Three);
for( $i=0 ; $i <= $#list ; $i++ ){
print $list[$i], "\n";
}

foreach 文を使って配列にアクセス

foreachを使って書き直したものが次の例です

@list = (One, Tow, Three);
foreach my $value ( @list ){
  print $value, "\n";
}

配列の超絶テクニック大全集

文字列から配列をつくる

配列を作る簡単な作り方は split を使ってデータをそのまま配列にする方法です。

$values = 'One,Two,Three';
@array = split( /,/, $values );
print $array[0];
> One
print "@array";
> One Two Three

配列から特定の要素を確かめる

配列の要素が文字列である場合の最も速いやり方は、配列の値をキーとしたハッシュを用意する方法です。

@array = qw/rhythm factory express biztech/;
%tmp;
for ( @array ) { $tmp{$_} = 1 }

こうすれば、$tmp{$some_value}があるかをチェックすることで、配列の要素を確かめることができます。

配列の値のすべてが小さな整数であれば、単純な添え字付き配列を使うことができます。

@array = (1, 2, 4, 8, 16, 32);
@tmp;
for ( @array ) { $tmp[$_] = 1; }

こうすれば、$tmp[$some_value]があるかをチェックすることで、配列の要素を確かめることができます。

配列から重複した要素を抜き取る

配列からユニークな要素だけを取り出す方法は、決まった順序で取り出したいのか、配列に格納されている順序がどうであるのかによります。

ソートされている配列からユニークな要素だけを取り出す
#! /usr/local/bin/perl
@array = (1,2,3,3,4);
$x = '-';
@sort = grep( $_ ne $x && ($x = $_), @array );
print @sort, "\n";
> 1234
ソートされていない配列からユニークな要素だけを取り出す
@array = (1,3,3,2,4);
%tmp;
@sort = grep(  !$tmp{$_}++, @array );
print @sort, "\n";
> 1324
ソートされていない配列からユニークな要素だけを取り出す(配列が小さな整数のみ)
@sort = grep(  !$tmp[$_]++, @array );

配列をスタックやキューとして使う

Perlには、配列の先頭や末尾に対して要素を追加、削除するためのshift、unshift、push、pop という4つの関数があります。

- 要素を追加 要素を取り除く
先頭に unshift shift
末尾に push pop

unshift

for ($i=5; $i<10; $i++) {
unshift(@a, $i);  # @a の最初に $i を追加
}
print "@a";
> 9 8 7 6 5

push

for ($i=0; $i<5; $i++) {
  push(@a, $i);  # @a の最後に $i を追加
}
print "@a";
> 0 1 2 3 4

unshift、pushは要素を加えていくときに使います。unshiftは最初に加え、pushは最後に加えます。

pop

@a = (9,8,7,6,5);
for ($i=0; $i<5; $i++) {
  print pop(@a);
}
> 56789

shift

@a = (9,8,7,6,5);
for ($i=0; $i<5; $i++) {
  print shift(@a);
}
> 98765

pop、shiftは取り出すときに使います。popはうしろから、shiftは先頭から取り出します。

ファイルを逆順に表示

スタックとキューを使ってファイルを逆順に表示するプログラムを作ります。

# 順に配列に入れる
while(<>){
  push(@buf,$_);
}
while(@buf){  # @buf は配列の長さを返す
  print pop(@buf);  # 後ろから取り出す
}

2つの配列の差、もしくは共通する要素数を調べる

2つの配列の差、もしくは共通要素を求めるには、ハッシュを使います。ただし、与えられた配列の要素には重複がないと仮定しています。

@array1 = (1,2,4,8,16,32);
@array2 = (1,2,4,8,16,64);
@intersection; @difference; %count = ();
foreach $element (@array1, @array2) { $count{$element}++ }
foreach $element (keys %count) {
  push @{ $count{$element} > 1 ? \@intersection : \@difference },
$element;
}
print "差:", $#difference + 1,"\n";
> 差: 2
print "共通:", $#intersection + 1,"\n";
> 共通: 5

配列から条件にマッチする要素を見つける

最初に見つけた要素のインデックス保存する方法が一般的です。

@array = qw(Sun Mon Tue Wed Thu Fri Sat);
for ( $i=0; $i < @array; $i++ ) {
  if ( $array[$i] eq "Mon" ) {    # "Mon"という値であれば
    $index = $i;    # インデックスを保存して
    last;    # for ループを終了
  }
}
print $array[$index], "\n";
> Mon

配列からランダムに要素を選択する

配列からランダムに要素を選択するには、rand()関数を使います。

srand;    # rand() 用のシード値を用意(5.004以降では不要)
@array = 1 .. 100;  # 配列の用意
$index   = rand @array;
print $array[$index], "\n";
> 38

配列の要素の順番をランダムする

カレントの要素をランダムに取り出した別の要素と交換します

srand;    # rand() 用のシード値を用意
@array = 1 .. 10;  # 配列の用意
@new;
while ( @array ) {
	push( @new, splice(@array , rand @array , 1) );
}
print @new, "\n";
> 82173910546

大きな配列に対しては、以下の方法がよいでしょう。

srand;    # rand() 用のシード値を用意(5.004以降では不要)
@array = 1 .. 100;  # 配列の用意
@new;
for( @array ){
	my $r = rand @new+1;
	push(@new, $new[$r]);
	$new[$r] = $_;
}
print "@new\n";

多次元配列を静的に作成

多次元配列とは、配列を要素とした配列のことです。次のような2次元配列はよく使われる例です。

@list = (
	[ "LUCY", "LIU" ],
	[ "CAMERON", "DIAZ" ],
	[ "DREW", "BARRYMORE", "produced by drew" ],
);
print $list[0][0], " & ", $list[1][0], " & ", $list[2][0];
> LUCY & CAMERON & DREW
print $list[2][2];
> produced by drew

多次元配列にアクセス

# 要素に値を代入
$list[0][0] = "Jini";
# リファレンスを使って全体を表示
for my $ref( @list ){
	print "@$ref\n";
}
# 1つずつ全体を表示(シンプルな方法)
for my $ref( @list ){
	for $ref2( @{$ref} ){
		print $ref2, "\n";
	}
}
# 1つずつ全体を表示
for my $i( 0 .. $#list ){
	for $j ( 0...$#{$list[$i]} ){
		print "$i $j : $list[$i][$j]\n";
} }

リスト操作関数

関連記事