お久しぶりです、三日坊主のXuです。
最近何かとサーバーサイドの処理を書くことが増えたので、bashコマンドよりも自由度が高く、高速高性能で文書処理できる言語がないかと探していた時にPerlに出会いました(今時Perlかよとか言ってたのごめんなさい)。
今までの記事との方向も大きく変わるかと思いますが、今回はPerlの基礎について書きたいと思います。
まだ学習途中なので随時更新します。
また、もし間違いがありましたら指摘していただけると助かります!
Perlについて
全称:Practical Extractional and Report Language
Pathologicial Ecletic Rubbish Listとも言われたりする()
Perlの特徴:
高速で文書処理を行う開発が行える。
インタプリタとコンパイラの両方の特徴を持つ。
「同じ目的を達成するためのルートは一つだけじゃない」という設計理念のもと作られている。
今回扱うバージョン:
Strawberry Perl 5.32.1.1
PATH:
- Windows:
[perl_dir]\site\bin, [perl_dir]\bin
- Linux:
which
コマンドで特定しましょう(ファイルを実行する際に755 permissionを付与する必要があります。)
基本的な文法
変数宣言
まずはとりあえずHelloworldを出力してみましょう。
print "Helloworld\n";
print("Helloworld");
Helloworld
Helloworld
このように、インタプリタ言語とコンパイラ言語両方で同じものを書くことができます。
レキシカル変数
いわゆるローカル変数です。
Perlの変数宣言には「my」と「our」と「local」の三つがあります。
それぞれ「レキシカル変数」、「パッケージ変数」、「ローカル変数」と呼ばれています。
Perlで変数を宣言するときは「our」や「local」の使用はできる限り避け、「my」のみでほぼすべての処理に対応できるようになっています。
my $num;
$num = 5;
print($num);
>>> 5
スカラ変数の初期化
my $num = 5;
配列変数の宣言
my @nums = (1, 2, 3);
ハッシュ変数の宣言
my %score = (math => 80, english => 70);
スコープ(block)
Blockともいいます。
{
my $num = 2;
}
# ここからは「$num」を利用できない。
User Input
$arg = $ARGV[0];
$echo = <STDIN>;
print("Arg result: $arg\n");
print("Echo result: $echo");
$ARGV
ほかの言語でもよく使われるargs可変長引数です。
以下のようにファイル名の後に値を入力すると受け取ってくれます。
$ perl <program name> test1
実行途中でこちらが何か入力するのを待ってほしいときは<STDIN>
を使います。
$ perl [program name] test1
> test2
>>> Arg result: test1
>>> Echo result: test2
Import modules
モジュール名を指定
use CGI;
通常、Perlでは定義されていない変数に対してundefinedエラーを返しませんが、
my $num = 1;
print($nam);
strictモジュールとmyによる変数宣言を一緒に使うと、変数宣言がない場合に変数を使おうとした場合に、コンパイルエラーになります。
use strict;
my $num = 1;
# 変数名が間違っているのでコンパイルエラーになる
print($nam);
>>> Global symbol "$nam" requires explicit package name (did you forget to declare "my $nam"?)
関数のインポート
use File::Basename 'basename', 'dirname';
Importを実行しない場合
use File::Basename ();
同じ結果を得るのに複数方法あるのはワクワクしますね。
use constant result => 100;
print("You got: ", result, "\n");
=
use feature 'say';
my $result = 100;
say "You got: $result";
条件分岐
If文
C言語などとほぼ変わらないが、else if
やelif
ではなく、elsif
if ([EXP_1]) {
[code_1];
} elsif ([EXP_2]) {
[code_2];
} else {
[code_3];
}
Unless
if notに当たります。
use strict;
my $data = 1;
unless($data < 0){
$data--;
};
print($data);
>>> 0
真理値
Perlでの真理値は0
、""
、"0"
がfalse
に該当し、
上記以外がtrue
に該当します。
use constant { false => 0 != 0, true => 0 == 0 };
my @ary = (false, true);
foreach my $a(@ary){
if ( $a ) {
print "($a):true\n";
}
else {
print "($a):false\n";
}
}
# ここではfalseは""として表示されます。
>>> ():false
>>> (1):true
ループ
For文
JavaScriptをやったことある人は既視感あるんじゃないかと思います。
for (my $count = 0; $count < 5; $count++){
print("$count \n");
}
While文
お馴染みの条件式がFlaseになるまで続くループです。
ほかの言語と違って抜け出すときはbreak
ではなくlast
です。
while (1) {
if ([EXP]) {
last;
}
}
または
while (1) {
last if [EXP];
[code];
}
do-while
While文との違いとして先にdoブロックの内容を実行して、while()
内の処理を実行してから止めるかどうかの判断を行います。
my $i = 4;
do {
print($i);
}
while ($i--);
print("\n", $i);
# while文であれば0は表示されません。
>>> 43210
# 最後に$i--を一回実行してから判断してます。
>>> -1
do-until
本質的にdo-While文と変わらない感じで、条件式を最後に評価していますが、違いとしてはフラグの処理をループ内に書けるため、比較自由度が高いと感じます。
my $i = 4;
do {
print($i);
$i--
}
until ($i < 0);
print("\n",$i);
>>> 43210
>>> -1
多重ループから脱出する
$finish
というフラグを作って置き、$num
の値をチェックするループを抜けた際に$finish
に値1
を付与し、存在確認の式を満たした際に外枠ループを抜けるというロジックになっております。
my $num = 0;
while (1) {
my $finish;
while (1) {
if ($num == 10) {
$finish = 1;
last;
}
$num++;
}
if ($finish) {
last;
}
}
redo
redoは演算子はループの最初に戻ことを意味しています。
この際、ループ内での操作のため、条件式は評価されず、以下のプログラムであれば本来$i = 0
の時点でループが止まるはずだが、スキップされているため$i = -1
の時にやっとループが中止されます。
use strict;
my $i = 5;
while ($i > 0) {
$i--;
if($i == 1){
redo;
}
print("$i");
}
print("\n$i");
>>> 4320
>>> 0
next
redo
と対照になるのがnextです。
今回はnext;
以降の処理をスキップしているため、条件式は評価されます。
use strict;
my $i = 5;
while ($i > 0) {
$i--;
if($i == 1){
next;
}
print("$i");
}
print("\n$i");
>>> 4320
>>> 0
continue block
条件式が再評価される直前に実行されるブロックを定義することができます。
continue
で繋がっているブロックは一つのブロックとして判断されます。
一つのブロック内で十分なことがほとんどなので可読性が低くなるため、できる限り使わないほうがいいと思います。
use strict;
my $i = 5;
{
print("roop_1\n");
{
print("roop_2\n");
$i--;
} continue {
redo if $i > 0;
print("$i\n");
}
}
>>> roop_1
>>> roop_2
>>> roop_2
>>> roop_2
>>> roop_2
>>> roop_2
>>> 0
List
表記
Perlではかっこ()
またはqw()
でリストを表現しています。
文字列と一緒にprintすることも可能です。
my @numbers = (1, 2, 3);
print(@numbers);
print("a", "b", "c", (1, 2, 3));
print(qw(1, 2, 3));
print((1, 2, 3)[2]);
>>> abc123
>>> abc123
>>> abc123
>>> 3
要素の追加と削除
要素を追加する際は直接インデックスを指定して追加することができます。
削除の場合はdeleteメソッドで削除できます。
my @numbers = (1, 2, 3);
$numbers[3] = 4;
print(@numbers, "\n");
delete($numbers[1]);
print(@numbers);
>>> 1234
>>> 134
また、Pythonなどと同じく後ろから値を取り出すことができます。
スカラ変数($
で定義された変数)に代入することでリストのサイズを得ることができます。
my @letters = ("a", "b", "c");
my $var = @letters;
print($letters[-1], "\n");
print($var, "\n");
# 最大インデックス
print($#var, "\n");
>>> c
>>> 3
>>> 2
Forech文で一個づつ取り出す
for文のパートでは言及しなかったが、foreach文を使うことでリスト内の要素を順序取り出すことができます。
foreach my $<var> (@<list>){...}
my @numbers = (1, 2, 3);
foreach my $num (@numbers) {
print("$num", "\n");
}
>>> 1
>>> 2
>>> 3
Hash
いわゆる辞書型です。keyとvalueによって構成されます。
Key, Valueの入出力
use strict;
my %dict = (
data1 => "fun",
data2 => "boring"
);
print("Learning English is so $dict{data1} \n");
> Learning English is so fun
まとめて出力する際はかなり楽、結果の値はすべてくっつけて出力しています。
my @dict_keys = keys %dict;
my @dict_values = values %dict;
my $dict_size = %dict;
print("Keys:", @dict_keys, "\n");
print("Values:", @dict_values, "\n");
print("Size:$dict_size");
> Keys:data1data2
> Values:funboring
> Size:2
listと同じように、追加と削除、そしてforeach文でkeyとvalueそれぞれ取り出すことができます。
$dict{data3} = "easy";
delete($dict{data2});
foreach my $key (keys(%dict)) {
print("$dict{$key}\n");
}
>>> fun
>>> easy
サブルーチン
いわゆるユーザー定義関数です。
何度も呼び出して同じ処理を行う場合に使います。
例えば以下のプログラムでは@data = (1,2,3)
というリストを作成し、各要素をそれぞれ1足した結果を出力し、@dataリーバスしたものを@rev_data
として同じ処理を行うものです。
ここではchangeVlue()
というサブルーチン(関数)を定義して実行しています。
use strict;
my @data = (1, 2, 3);
&changeValue(@data);
my @rev_data = reverse(@data);
&changeValue(@rev_data);
sub changeValue{
my @array = @_;
foreach my $num (@array) {
$num++;
print($num);
}
print("\n");
}
Perlの特殊変数
コマンドライン引数 @ARGV
前述したとおり、コマンドラインからスクリプトに値を渡すために使います。
デフォルト変数 $_
スカラ変数が指定されるべき場所で、変数を省略した場合に代替で利用される変数です。
下記コードの実行結果はどれも同じ結果が得られます。
use strict;
my @data = (1, 2, 3, 4);
for(@data){
print($_);
}
print("\n");
for(@data){
print;
}
print("\n");
print "$_" for @data;
>>>1234
サブルーチンの引数 `@_
サブルーチン内で値を引き継ぐ引数。
環境変数 %ENV
環境変数にアクセスできます。
環境変数名がkeyのhashになってますので$ENV{<var_name>}
で値を確認できます
プログラム名 $0
絶対パスからプログラムを起動した場合は絶対パス、カレントディレクトリからのっばいは相対パスを得ることができる。
Debug
例外処理
die関数
エラーメッセージを出力しPerlスクリプトを終了します。
例外を発生させる
die $message;
> Died at excpt.pl line 1.
プログラム名 line 行番号の形式で表示します。
die("Error");
> Error at excpt.pl line 1.
エラーメッセージを合わせて出力
$!
はシステムに対する要求を行った時に発生したエラー
die("Error $!");
eval関数
例外をキャッチしてプログラムが終了するのを防ぐことができます。
eval { 例外が発生する処理 };
通常division by zero
で終了するプログラムを実行しても何のエラー表示しません。
use strict;
eval {
my $a = 1/0;
};
エラー情報$@
を出力するには
print $@;
>>> Illegal division by zero at expt.pl line 4.
プログラム実行
Warning制御
- スクリプトを走らせる際に
-w
を入れることで警告を表示させることができます。
$perl -w <program name>
- スクリプト内で先頭に
#!/usr/bin/perl -w
を追加する。 - Perl5.6及び以降のバージョンでのみuse warningsプラグマで呼び出すことができます。
#!/usr/bin/perl -w
use warnings
どの部分に対してどの警告が有効か設定できるため、より柔軟性が高いと言えます。
Debug build
プログラムを実行せずにDebugだけを行う場合
$perl -c <program name>
上記二つを同時に行いたい場合は
$perl -cw <program name>