はじめに
Perlを全く勉強したことがありませんでしたが触れる機会がありました。
バックエンドの言語にはいくつか触れてたのでなんとな~く「こういうことかな?」というのは見当がつきますが、記号や記法が独特で読みづらく詳細まではわからないので重い腰をあげてPerlの勉強をしました。
ネットで検索するPerlの記事のほとんどは2010年代前半で令和の時代に需要はほとんどないかもしれませんが、これから触れる機会がある人たちに最低限必要であろう基本的な部分をまとめました。
この記事ではよく使われてそうな文法や記法について個人の主観でまとめてます。
パッケージやリファレンス/デリファレンスについてはまだ理解できていないのでまとめられていません。
モチベーションと気力があればいずれやるかも…(やらない可能性の方が大きい)
Perlの記法・文法
スカラー値
$num = 10;
$str = "hoge";
- 単独の数値や文字列をスカラー値と呼ぶ
-
$
を先頭につけてスカラー値を代入したものをスカラー変数と呼ぶ
真偽値
真 | 偽 |
---|---|
右以外の全て | 数値の0 文字列の'0' 空文字列 undef |
-
true,false
といったものはなく基本的には0,1
で判定する
defined関数
defined($hoge)
- 例えば$hogeが未定義でundefのとき偽、undefじゃないとき真になる
配列とリスト
@fruits = ("apple", "banana", "remon");
print "$fruits[0]\n";
# apple
- スカラー値の集合に順序(index)をつけたものをリストと呼ぶ
-
("apple", "banana", "remon")
がリスト
-
-
@
を先頭につけてリストを代入したものを配列と呼ぶ-
@fruits
が配列
-
- 要素の取り出しは
$fruits[0]
と記述する-
@fruits
配列の要素はスカラー値のため@fruits[0]
ではなく$fruits[0]
となっているのがポイント
-
- リストの個数は
$len = @fruits;
のように配列をスカラーで受け取る- Perlのコンテキストという概念により配列がスカラー値に変換される
- この場合だと
$len
は3
になる
- インデックスの最大値は
$len = $#fruits
で取得する- この場合だと
$len
は2
になる
- この場合だと
qwを使ったリストの書き方
リスト("apple", "banana", "remon")
をqwショートカットと呼ばれる記法で以下のように書けます。
qw( apple banana remon );
qw{ apple banana remon };
qw/ apple banana remon /;
qw# apple banana remon #;
- クォート記号が不要になる(クォートを入れるとクォートも文字列として含む)
- 中でも
qw/ /
の形がよく使われているかも
囲い記号のデリミタが色々使えますが意味はすべて同じです。
例えば要素に/usr/bin/
などのパスを含める場合はqw/ /
ではなくqw( )
を使うといった使い分けが想定されます。
分割代入
@fruits = qw/ apple banana remon /;
($fruit1, $fruit2, $fruit3) = @fruits;
# $fruit1はapple
# $fruit2はbanana
# $fruit3はremon
- 変数の数が多くリストの数と一致しない場合、はみだした変数は
undef
になる - リストの数が多く変数の数と一致しない場合、はみだしたリストは切り捨てられる
pop関数
popは配列の末尾の要素を取り除いてその値を返します。
返り値を受け取らない場合は単に末尾の要素を削除するだけになります。
@fruits = qw/ apple banana remon /;
$fruit1 = pop(@fruits);
$fruit2 = pop @fruits;
# $fruit1はremon
# $fruit2はbanana
# @fruitsはqw/ apple /
- 括弧
()
省略可
push関数
pushは配列の末尾に新しい要素を追加します。
@fruits = qw/ apple banana remon /;
push(@fruits, "melon");
push @fruits, "orange", "grape";
# $fruits[3]はmelon
# $fruits[5]はgrape
# @fruitsはqw/ apple banana remon melon orange grape /
- 括弧
()
省略可 - 第1引数は配列、第2引数以降は追加する要素(スカラー値、リスト、配列)
-
push @fruits, qw/ orange grape /;
第2引数はリストでもOK -
push @fruits, @hoge;
第2引数は配列でもOK
-
shift関数
shiftは配列の先頭の要素を取り除いてその値を返します。
popの逆です。
後述するサブルーチンで引数をプライベート変数に代入する際の頻出です。
@fruits = qw/ apple banana remon /;
$fruit1 = shift(@fruits);
$fruit2 = shift @fruits;
# fruit1はapple
# fruit2はbanana
# @fruitsはqw/ remon /
- 括弧
()
省略可
unshift関数
unshiftは配列の先頭の要素に新しい要素を追加します。
pushの逆です。
@fruits = qw/ apple banana remon /;
unshift(@fruits, "melon");
unshift @fruits, "orange", "grape";
# $fruits[0]はorange
# $fruits[2]はmelon
# @fruitsはqw/ orange grape melon apple banana remon /
- 括弧
()
省略可
splice関数
spliceは配列の任意の位置に値を追加したり取り除いたりします。
- 引数は4つで3つ目以降は省略可
- 第1引数には配列を指定する
- 第2引数には処理の開始位置(index)を指定する
- 括弧
()
省略可
■引数が2つの場合
引数2つの場合は処理の開始位置から末尾までの要素を取り除いてそれらを返します。
@fruits = qw/ apple banana remon melon orange grape /;
@removed = splice @fruits, 2;
# @fruitsはqw/ apple banana /
# @removedはqw / remon melon orange grape /
■引数が3つの場合
引数3つの場合は第3引数に長さを指定して、処理の開始位置から長さ分の要素を取り除いてそれらを返します。
@fruits = qw/ apple banana remon melon orange grape /;
@removed = splice @fruits, 2, 4;
# @fruitsはqw/ apple banana orange grape /
# @removedはqw / remon melon /
■引数が4つの場合
引数4つの場合は第4引数にリストを指定して、指定位置に挿入します。
@fruits = qw/ apple banana remon melon orange grape /;
@removed = splice @fruits, 3, 1, qw/ watermelon /;
# @fruitsはqw/ apple banana remon watermelon orange grape /
# @removedはqw / melon /
第4引数はリストでなくてもsplice @fruits, 1, 1, "hoge", "fuga"
のように第4引数以降に列挙すれば挿入はされますがPerlは書き方に自由度がありすぎるのでリストか配列を渡す方がコードもスッキリして見やすいと思います。
foreach
お馴染みのforeachです。
foreach $fruit (@fruits) {
print "$fruit\n";
}
-
@fruits
配列の要素を制御変数$fruit
が受け取ってループ処理を行う - 配列は括弧
()
で囲む必要がある
ここで制御変数$fruit
を省略することができます。
省略する代わりにデフォルト変数$_
を使って以下のように書き換えられます。
foreach (@fruits) {
print $_ . "\n";
}
- 制御変数を省略した場合は
$_
が配列の中身を受け取ってループ処理を行う
初見では突然出てきた$_
が不自然でたまりませんでした。
for
Perlではfor
とforeach
の記述の違いによる動作の違いはありません。
具体的に言うと上述のforeach
のコードをfor
に書き換えても同じように動作するということです。
for文も馴染みのある一般的な書き方です。
for ($i=0; $i<10; $i++) {
print "\$i=$i\n";
}
# $i=0
# $i=1
# ...略...
# $i=9
for
とforeach
との動作の区別は括弧()
内にセミコロン;
が2個あるかどうかで判断しているそうです。
- ループ処理には他にも
while
やuntil
やラベル付きブロックが使える -
last
:ループを途中で終了する(他言語だとbreak
が一般的かも) -
next
:その時点のループ処理をスキップする(他言語だとcontinue
が一般的かも)
if
Perlは数値の比較と文字列の比較で比較演算子が異なります。
比較 | 数値の比較 | 文字列の比較 |
---|---|---|
等しい | == | eq |
等しくない | != | ne |
より小さい | < | lt |
より大きい | > | gt |
以下 | <= | le |
以上 | >= | ge |
$num = 3;
if ($num > 1) {
print "$numは1より大きい";
}
# 3は1より大きい
$str = "hogd";
if ($str gt "hoge") {
print "文字列ソート順では$strはhogeより後である";
} else {
print "文字列ソート順では$strはhogeより前である";
}
# 文字列ソート順ではhogdはhogeより前である
- 文字列の大小比較では、比較する文字列のソート順で真偽判定をしている
- 条件式が細分化される場合は
elsif (条件式) {}
で記述する - 三項演算子
? :
もある
この辺の書き方は他の多くの言語と変わりませんね。
if文の変わった使い方として文末に書くことができます。
for ($i=0; $i<10; $i++) {
print "\$i=$i\n";
last if $i == 5;
}
例えばこの例だと$i==5
になったときにループ処理を終了するといったものになります。
下記を略して書いたものです。
if ($i == 5) {
last;
}
サブルーチン
他の言語でいう関数です。
sub sum {
my ($a, $b) = @_;
return $a + $b;
}
&sum(1, 3) # 4
-
sub サブルーチン名 {}
でサブルーチンを定義できる - Perlの引数はリストで渡されて
@_
に引数リストが格納されている -
my ($a, $b) = @_
は配列の項で見た分割代入をしている- 引数が1つの場合でも括弧
()
は必須 -
()
がない場合は引数リストの長さが代入される
- 引数が1つの場合でも括弧
-
my
はこのサブルーチン内でのみ使うプライベートな変数(レキシカル変数)であることを宣言している - 戻り値は
return
で記述するが、省略した場合は最後に評価された式が戻り値になる - サブルーチンは
&
をつけて呼び出す- ただしPerlの組み込み関数と同名のサブルーチン名でなければ
&
も省略可 - Perlの組み込み関数一覧の参考サイト:https://perldoc.jp/index/function
- ただしPerlの組み込み関数と同名のサブルーチン名でなければ
引数が1つのサブルーチン
引数が1つの場合は括弧()
が必須と書きましたが、配列操作のshift
を使って以下のようにも書くこともできます。
my $hoge = shift;
-
my $hoge = shift @_;
が元々の形で、サブルーチン内ではshift関数
の引数が暗黙的に@_
となるため@_
を省略できる -
shift関数
で返ってくる値はスカラー値になるためリストの分割代入ではなく、単にスカラー値の代入になることで()
が不要になる
引数が1つのときのこの記法my $hoge = shift
は頻出です。
ハッシュ
言語によってはオブジェクトと言ったり連想配列と言ったりするkeyとvalueをまとめたアレです。
%fruits = (
apple => 2,
banana => 3,
remon => 1,
);
print $fruits{apple}; # 2
- ハッシュのvalueを取得するときは
$fruits{apple}
のようにブレース{}
でkeyを囲む - key名にはよく英数字、アンダースコア
_
を使うがkey名の先頭が数字でなければ$fruits{"apple"}
ではなく$fruits{apple}
のようにブレース{}
内のクォートを省略できる -
key => value
のように=>
を使うとkeyのクォートを省略できる-
=>
は,
でも構わないため("apple", 2, "banana", 3, "remon", 1)
とも書けるが分かりやすさのために=>
を用いている - 実は
"apple" => 2
が元々の形だが省略記法によりしばしばクォートが略されている
-
- ハッシュの中にはkeyとvalueのペアがランダムな順序で入っており、keyとvalueのペアを取り出すときは入れた通りの並びで出てくるとは限らない
- 実際には
(remon => 1, apple => 2, banana => 3)
という順序で入っているかもしれない
- 実際には
keys関数とvalues関数
%fruits = (
apple => 2,
banana => 3,
remon => 1,
);
@k = keys %fruits;
@v = values %fruits;
-
@k
にはkeyが何らかの順序でリスト化される-
qw/ banana apple remon /
かもしれないしqw/ apple remon banana /
かもしれない
-
-
@v
にはvalueが何らかの順序でリスト化される-
qw/ 3 2 1 /
かもしれないしqw/ 2 1 3 /
かもしれない
-
- ただしkeysとvaluesでリストを返したとき、中身の順序はわからないがkeyとvalueのindexは一致する
この何らかの順序でリスト化されるというのが先に述べたランダムな順序で入っていることに起因しています。
each関数
eachはkeyとvalueの2要素のリストを取り出します。
%fruits = (
apple => 2,
banana => 3,
remon => 1,
);
while ( ($key, $value) = each %fruits ) {
print "$key => $value\n";
}
# apple => 2
# banana => 3
# remon => 1
-
($key, $value) = each %fruits
でeach
使って分割代入している - 順に中身を渡していって中身がなくなると空リストとなり
$key, $value
ともにundef
となる -
undef
となれば偽と判定されループが終了する
正直each
の挙動はよくわかってないです。。。
ただ使い方としてはほとんどこのようなwhile
ループで使われるようです。
exists関数
%fruits = (
apple => 2,
banana => 3,
remon => 1,
);
if (exists $fruits{apple}) {
print "appleが$fruits{apple}個あります\n";
}
# appleが2個あります
- ハッシュの中に指定されたkeyが存在するかを確認する
- valueの真偽に関わらずあくまでkeyの有無で真偽を返す
delete関数
%fruits = (
apple => 2,
banana => 3,
remon => 1,
);
delete $fruits{apple};
while ( ($key, $value) = each %fruits ) {
print "$key => $value\n";
}
# remon => 1
# banana => 3
- ハッシュから指定されたkeyとvalueを削除する
- 指定されたkeyが元々ない場合は何もしない
まとめ
Perlの思想は「やり方は1つじゃない」です。
そのためか「こういう風にも書けるぞ」という略した記法が非常に多いです。
$_
や@_
、サブルーチンで引数が一つのときのshift
など何も知らずにPerlを読むと突然出てくるこいつらに戸惑います。
こういったPerlの作法をまず知ること(という苦行)がPerlの理解の第一歩な気がします。
ここに書いた基本的な文法は読むためのもので実際に書くためのものは省略しています。
新たにPerlを書くためにはファイルの最初にシバンやversionの記載が必要だったりします。
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
Perlを新たに書き始める人は少ないと思うので、最低限読むための基本中の基本をまとめてみました。
これに加えモジュールやリファレンス/デリファレンスの内容がわかれば、Perlはだいぶ読めるものになってくると思います(自戒)。
というわけでPerlの基礎でした。