Perlを読むために基本文法を知りたい人へ

# はじめに
Perlを全く勉強したことがありませんでしたが触れる機会がありました。
バックエンドの言語にはいくつか触れてたのでなんとな～く「こういうことかな？」というのは見当がつきますが、記号や記法が独特で読みづらく詳細まではわからないので重い腰をあげてPerlの勉強をしました。
ネットで検索するPerlの記事のほとんどは2010年代前半で令和の時代に需要はほとんどないかもしれませんが、これから触れる機会がある人たちに最低限必要であろう基本的な部分をまとめました。

この記事ではよく使われてそうな文法や記法について個人の主観でまとめてます。
パッケージやリファレンス/デリファレンスについてはまだ理解できていないのでまとめられていません。
モチベーションと気力があればいずれやるかも…(やらない可能性の方が大きい)


# Perlの記法・文法
## スカラー値
```perl:スカラー値
$num = 10;
$str = "hoge";
```
- 単独の数値や文字列をスカラー値と呼ぶ
- `$`を先頭につけてスカラー値を代入したものを**スカラー変数**と呼ぶ


## 真偽値
|真|偽|
|-|-|
|右以外の全て|数値の0<br>文字列の'0'<br>空文字列<br>undef|
- `true,false`といったものはなく基本的には`0,1`で判定する

## defined関数
```perl:defined例
defined($hoge)
```
- 例えば$hogeが未定義でundefのとき偽、undefじゃないとき真になる



## 配列とリスト
```perl:配列とリスト例
@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ショートカットと呼ばれる記法で以下のように書けます。
```perl:qw使用例
qw( apple banana remon );
qw{ apple banana remon };
qw/ apple banana remon /;
qw# apple banana remon #;
```
- クォート記号が不要になる(クォートを入れるとクォートも文字列として含む)
- 中でも`qw/ /`の形がよく使われているかも

囲い記号のデリミタが色々使えますが意味はすべて同じです。
例えば要素に`/usr/bin/`などのパスを含める場合は`qw/ /`ではなく`qw( )`を使うといった使い分けが想定されます。


### 分割代入
```perl:分割代入例
@fruits = qw/ apple banana remon /;
($fruit1, $fruit2, $fruit3) = @fruits;
# $fruit1はapple
# $fruit2はbanana
# $fruit3はremon
```
- 変数の数が多くリストの数と一致しない場合、はみだした変数は`undef`になる
- リストの数が多く変数の数と一致しない場合、はみだしたリストは切り捨てられる

### pop関数
popは配列の末尾の要素を取り除いてその値を返します。
返り値を受け取らない場合は単に末尾の要素を削除するだけになります。
```perl:pop使用例
@fruits = qw/ apple banana remon /;
$fruit1 = pop(@fruits);
$fruit2 = pop @fruits;
# $fruit1はremon
# $fruit2はbanana
# @fruitsはqw/ apple /
```
- 括弧`()`省略可


### push関数
pushは配列の末尾に新しい要素を追加します。
```perl: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の逆です。
後述するサブルーチンで引数をプライベート変数に代入する際の頻出です。
```perl:shift使用例
@fruits = qw/ apple banana remon /;
$fruit1 = shift(@fruits);
$fruit2 = shift @fruits;
# fruit1はapple
# fruit2はbanana
# @fruitsはqw/ remon /
```
- 括弧`()`省略可


### unshift関数
unshiftは配列の先頭の要素に新しい要素を追加します。
pushの逆です。
```perl:unshift使用例
@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つの場合は処理の開始位置から末尾までの要素を取り除いてそれらを返します。
```perl:引数2つのsplice例
@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引数に長さを指定して、処理の開始位置から長さ分の要素を取り除いてそれらを返します。
```perl:引数3つのsplice例
@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引数にリストを指定して、指定位置に挿入します。
```perl:引数4つのsplice例
@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です。

```perl:foreach例
foreach $fruit (@fruits) {
  print "$fruit\n";
}
```
- `@fruits`配列の要素を制御変数`$fruit`が受け取ってループ処理を行う
- 配列は括弧`()`で囲む必要がある

ここで制御変数`$fruit`を省略することができます。
省略する代わりにデフォルト変数`$_`を使って以下のように書き換えられます。
```perl:$_は制御変数の省略形
foreach (@fruits) {
  print $_ . "\n";
}
```
- 制御変数を省略した場合は`$_`が配列の中身を受け取ってループ処理を行う

初見では突然出てきた`$_`が不自然でたまりませんでした。



## for
Perlでは`for`と`foreach`の記述の違いによる動作の違いはありません。
具体的に言うと上述の`foreach`のコードを`for`に書き換えても同じように動作するということです。
for文も馴染みのある一般的な書き方です。

```perl: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 |

```perl:数値の比較
$num = 3;
if ($num > 1) {
  print "$numは1より大きい";
}
# 3は1より大きい
```

```perl:文字列の比較
$str = "hogd";
if ($str gt "hoge") {
  print "文字列ソート順では$strはhogeより後である";
} else {
  print "文字列ソート順では$strはhogeより前である";
}
# 文字列ソート順ではhogdはhogeより前である
```

- 文字列の大小比較では、比較する文字列のソート順で真偽判定をしている
- 条件式が細分化される場合は`elsif (条件式) {}`で記述する
- 三項演算子`? :`もある

この辺の書き方は他の多くの言語と変わりませんね。

if文の変わった使い方として文末に書くことができます。

```perl:文末のif
for ($i=0; $i<10; $i++) {
  print "\$i=$i\n";
  last if $i == 5;
} 
```
例えばこの例だと`$i==5`になったときにループ処理を終了するといったものになります。
下記を略して書いたものです。
```perl:last if $i == 5 と同義
if ($i == 5) {
  last;
}
```



## サブルーチン
他の言語でいう関数です。
```perl:サブルーチン例
sub sum {
  my ($a, $b) = @_;
  return $a + $b;
}

&sum(1, 3) # 4
```
- `sub サブルーチン名 {}`でサブルーチンを定義できる
- Perlの引数はリストで渡されて`@_`に引数リストが格納されている
- `my ($a, $b) = @_`は配列の項で見た分割代入をしている
  - 引数が1つの場合でも括弧`()`は必須
  - `()`がない場合は引数リストの長さが代入される
- `my`はこのサブルーチン内でのみ使うプライベートな変数(レキシカル変数)であることを宣言している
- 戻り値は`return`で記述するが、省略した場合は最後に評価された式が戻り値になる
- サブルーチンは`&`をつけて呼び出す
  - ただしPerlの組み込み関数と同名のサブルーチン名でなければ`&`も省略可
  - Perlの組み込み関数一覧の参考サイト：https://perldoc.jp/index/function


### 引数が1つのサブルーチン
引数が1つの場合は括弧`()`が必須と書きましたが、配列操作の`shift`を使って以下のようにも書くこともできます。
```perl:引数が1つのとき
my $hoge = shift;
```
- `my $hoge = shift @_;`が元々の形で、サブルーチン内では`shift関数`の引数が暗黙的に`@_`となるため`@_`を省略できる
- `shift関数`で返ってくる値はスカラー値になるためリストの分割代入ではなく、単にスカラー値の代入になることで`()`が不要になる

引数が1つのときのこの記法`my $hoge = shift`は頻出です。



## ハッシュ
言語によってはオブジェクトと言ったり連想配列と言ったりするkeyとvalueをまとめたアレです。
```perl:ハッシュ例
%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関数
```perl: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要素のリストを取り出します。

```perl:each例
%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関数
```perl:exists例
%fruits = (
  apple => 2,
  banana => 3,
  remon => 1,
);

if (exists $fruits{apple}) {
  print "appleが$fruits{apple}個あります\n";
}
# appleが2個あります
```
- ハッシュの中に指定されたkeyが存在するかを確認する
  - valueの真偽に関わらずあくまでkeyの有無で真偽を返す


### delete関数
```perl: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の記載が必要だったりします。
```perl:
#!/usr/bin/perl

use strict;
use warnings;
use 5.010;
```

Perlを新たに書き始める人は少ないと思うので、最低限読むための基本中の基本をまとめてみました。
これに加えモジュールやリファレンス/デリファレンスの内容がわかれば、Perlはだいぶ読めるものになってくると思います(自戒)。

というわけでPerlの基礎でした。

## 参考文献
[初めてのPerl 第7版](https://www.oreilly.co.jp/books/9784873118246/)