Perl入門

お久しぶりです、三日坊主の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を出力してみましょう。

```perl
print "Helloworld\n";
print("Helloworld");
```

```shell
Helloworld
Helloworld
```

このように、インタプリタ言語とコンパイラ言語両方で同じものを書くことができます。

### レキシカル変数

いわゆるローカル変数です。
Perlの変数宣言には「my」と「our」と「local」の三つがあります。
それぞれ「レキシカル変数」、「パッケージ変数」、「ローカル変数」と呼ばれています。
Perlで変数を宣言するときは「our」や「local」の使用はできる限り避け、「my」のみでほぼすべての処理に対応できるようになっています。

```perl
my $num;

$num = 5;
print($num);

>>> 5
```

### スカラ変数の初期化

```perl
my $num = 5;
```

### 配列変数の宣言

```perl
my @nums = (1, 2, 3);
```

### ハッシュ変数の宣言

```perl
my %score = (math => 80, english => 70);
```

### スコープ(block)

Blockともいいます。

```perl
{
  my $num = 2;
}
# ここからは「$num」を利用できない。
```

## User Input

```perl
$arg = $ARGV[0];
$echo = <STDIN>;

print("Arg result: $arg\n");
print("Echo result: $echo");
```

`$ARGV`ほかの言語でもよく使われるargs可変長引数です。
以下のようにファイル名の後に値を入力すると受け取ってくれます。
`$ perl <program name> test1`

実行途中でこちらが何か入力するのを待ってほしいときは`<STDIN>`を使います。

```shell
$ perl [program name] test1

> test2

>>> Arg result: test1
>>> Echo result: test2
```

## Import modules

### モジュール名を指定

```perl
use CGI;
```

通常、Perlでは定義されていない変数に対してundefinedエラーを返しませんが、

```perl
my $num = 1;

print($nam);
```

strictモジュールとmyによる変数宣言を一緒に使うと、変数宣言がない場合に変数を使おうとした場合に、コンパイルエラーになります。

```perl
use strict;

my $num = 1;

# 変数名が間違っているのでコンパイルエラーになる
print($nam);

>>> Global symbol "$nam" requires explicit package name (did you forget to declare "my $nam"?)
```

### 関数のインポート

```perl
use File::Basename 'basename', 'dirname';
```

### Importを実行しない場合

```perl
use File::Basename ();
```

同じ結果を得るのに複数方法あるのはワクワクしますね。

```perl
use constant result => 100;

print("You got: ", result, "\n");
```

＝

```perl
use feature 'say';
my $result = 100;

say "You got: $result";
```

## 条件分岐

### If文

C言語などとほぼ変わらないが、`else if`や`elif`ではなく、elsif

```perl
if ([EXP_1]) {
    [code_1];
} elsif ([EXP_2]) {
    [code_2];
} else {
    [code_3];
}
```

### Unless

if notに当たります。
```perl
use strict;

my $data = 1;

unless($data < 0){
    $data--;
};

print($data);

>>> 0
```

### 真理値

Perlでの真理値は`0`、`""`、`"0"`が`false`に該当し、
上記以外が`true`に該当します。

```perl
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をやったことある人は既視感あるんじゃないかと思います。

```perl
for (my $count = 0; $count < 5; $count++){
  print("$count \n");
}
```

### While文

お馴染みの条件式がFlaseになるまで続くループです。
ほかの言語と違って抜け出すときは`break`ではなく`last`です。

```perl
while (1) {
  if ([EXP]) {
    last;
  }
}
```

または

```perl
while (1) {
  last if [EXP];
  [code];
}
```

### do-while

While文との違いとして先にdoブロックの内容を実行して、`while()`内の処理を実行してから止めるかどうかの判断を行います。

```perl
my $i = 4;
do {
    print($i);
} 
while ($i--);

print("\n", $i);

# while文であれば0は表示されません。
>>> 43210
# 最後に$i--を一回実行してから判断してます。
>>> -1
```

### do-until

本質的にdo-While文と変わらない感じで、条件式を最後に評価していますが、違いとしてはフラグの処理をループ内に書けるため、比較自由度が高いと感じます。

```perl
my $i = 4;

do {
    print($i);
    $i--
} 
until ($i < 0);

print("\n",$i);

>>> 43210
>>> -1
```

### 多重ループから脱出する

`$finish`というフラグを作って置き、`$num`の値をチェックするループを抜けた際に`$finish`に値`1`を付与し、存在確認の式を満たした際に外枠ループを抜けるというロジックになっております。

```perl
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`の時にやっとループが中止されます。

```perl
use strict;

my $i = 5;

while ($i > 0) {
    $i--;

    if($i == 1){
        redo;
    }

    print("$i");
}

print("\n$i");

>>> 4320
>>> 0
```

### next

`redo`と対照になるのがnextです。
今回は`next;`以降の処理をスキップしているため、条件式は評価されます。

```perl
use strict;

my $i = 5;

while ($i > 0) {
    $i--;

    if($i == 1){
        next;
    }

    print("$i");
}
print("\n$i");

>>> 4320
>>> 0
```

### continue block

条件式が再評価される直前に実行されるブロックを定義することができます。
`continue`で繋がっているブロックは一つのブロックとして判断されます。
一つのブロック内で十分なことがほとんどなので可読性が低くなるため、できる限り使わないほうがいいと思います。

```perl
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することも可能です。

```perl
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メソッドで削除できます。

```perl
my @numbers = (1, 2, 3);
$numbers[3] = 4;

print(@numbers, "\n");

delete($numbers[1]);

print(@numbers);

>>> 1234
>>> 134
```

また、Pythonなどと同じく後ろから値を取り出すことができます。
スカラ変数（`$`で定義された変数）に代入することでリストのサイズを得ることができます。

```perl
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>){...}`

```perl
my @numbers = (1, 2, 3);

foreach my $num (@numbers) {
  print("$num", "\n");
}

>>> 1
>>> 2
>>> 3
```

## Hash

いわゆる辞書型です。keyとvalueによって構成されます。

### Key, Valueの入出力

```perl
use strict;

my %dict = (
    data1 => "fun",
    data2 => "boring"
);

print("Learning English is so $dict{data1} \n");

> Learning English is so fun 
```

まとめて出力する際はかなり楽、結果の値はすべてくっつけて出力しています。

```perl
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それぞれ取り出すことができます。

```perl
$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()`というサブルーチン(関数)を定義して実行しています。

```perl
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`
前述したとおり、コマンドラインからスクリプトに値を渡すために使います。

## デフォルト変数 `$_`
スカラ変数が指定されるべき場所で、変数を省略した場合に代替で利用される変数です。
下記コードの実行結果はどれも同じ結果が得られます。

```perl
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スクリプトを終了します。

例外を発生させる

```perl
die $message;

> Died at excpt.pl line 1.
```

プログラム名 line 行番号の形式で表示します。

```perl
die("Error");

> Error at excpt.pl line 1.
```

エラーメッセージを合わせて出力
`$!`はシステムに対する要求を行った時に発生したエラー

```perl
die("Error $!");
```

## eval関数

例外をキャッチしてプログラムが終了するのを防ぐことができます。

```perl
eval { 例外が発生する処理 };
```

通常`division by zero`で終了するプログラムを実行しても何のエラー表示しません。

```perl
use strict;

eval {
    my $a = 1/0;
};
```

エラー情報`$@`を出力するには

```perl
print $@;

>>> Illegal division by zero at expt.pl line 4.
```

# プログラム実行

## Warning制御

1. スクリプトを走らせる際に`-w`を入れることで警告を表示させることができます。
`$perl -w <program name>`
2. スクリプト内で先頭に
`#!/usr/bin/perl -w`
を追加する。
3. Perl5.6及び以降のバージョンでのみuse warningsプラグマで呼び出すことができます。

```perl
#!/usr/bin/perl -w

use warnings
```

どの部分に対してどの警告が有効か設定できるため、より柔軟性が高いと言えます。

## Debug build

プログラムを実行せずにDebugだけを行う場合

```shell
$perl -c <program name>
```

上記二つを同時に行いたい場合は

```shell
$perl -cw <program name>
```