Perl

プログラミングの基本が分かる入門者のとびら

Perlは、インタープリタ式のスクリプト言語で、プログラミング入門者から熟練のプログラマまで、多くのプログラマに広く使われている言語です。
実際のところ、「Perlを一度触るとプログラミングの基本が分かる」ということが言えます。
プログラミングを行う上で、プログラミングとはどのようなものなのか、どのようにコードを書いて実行するのか、ということが分かりやすいため、「入門者向き」と言えます。
PerlUNIXのフィルターコマンドやシェルスクリプトsedコマンドなどの考え方をベースとしているため、標準入出力やファイル処理など、Perlの教科書にはUNIXの用語が飛び交うこともしばしばです。そのため、UNIXの基本的な知識を知っていれば、習得の助けとなります。
また、PerlUNIXのフィルターコマンドと同様に、パイプなどで他のUNIXコマンドやシェルスクリプトと組み合わせて、Perlスクリプトを使えます。このため、「このコマンドで実行した結果を簡単に再処理したい」といった場合に、Perlで簡単なスクリプトを書いてその中に「はめこむ」といった使い方をすることができ、システム管理に役立ちます。
C/C++言語などと違い、Perlには変数の型指定がなく、型はその場その場で動的に決まります。ですから、

$sentence=("123"."567")x2;

として"123567123567"という文字列を作った後で、それを

$sentence+=3;

などとし、「数値を文字列にし、その上でまた数値にする」といったことができます。
Perl正規表現も強力で、

@list =(
  "This is a pen.",
  "This is an apple.",
  "It was good.",
  "They are gread people."
);

foreach $sentence(@list) {
  if($sentence =~ /is/) {
    print "$sentence\n";
  }
}

Perl教科書より編集して引用。)
のようにすることで、「リスト@listの中のisという文字列が含まれている要素を表示する」のようなことをパターンマッチングで処理することができます。
正規表現は、たとえばログファイルやUNIXの設定ファイルのように、「ある一定の形式で書かれているテキストを整形する」場合に役に立ちます。これはUNIXでは多くの場合タブや空白で区切られており、場合によってはawksedなどでも処理できるところを、より高度にPerlで行うこともできます。
PerlUNIXと親和性が高く、UNIXシステムでは最初からインストールされていることがほとんどですが、Windowsからも使えます。ActiveState社のActivePerlとStrawberry Perlがあり、僕個人の意見ではStrawberry Perlがおすすめです。ActivePerlのように導入のためにアカウントを作成する必要が無く、msiファイルだけをダウンロードして実行することで簡単に導入でき、必要なバイナリはgccなども含めて全てインストールされるため、CPANのあらゆるモジュールを利用できます。僕もWindowsにおけるPerlはStrawberry Perlを使っています。

ラリー・ウォールの遊び心がいっぱい

Perlの特徴として、Perlの作者のラリー・ウォールの遊び心がいっぱいである、ということが言えます。
各所に面白くするための工夫が見られて、省略など多くの「多様なやり方」があります。
UNIXとも親和性が高く、処理速度もスクリプト言語の中では高速なので、Perlの愛好家は今でもたくさんいます。
最近はPythonRubyなど新しい言語がどんどん増えていますが、彼らは「多様性よりも自然さ」を好み、「自然でない記述は書きづらくする」といったことを行っています。それが悪いわけでは全くありませんが、Perlの遊び心がPythonRubyでは失われているように感じます。

コンテキストによって変わる人間臭い言語

Perlの文法は文脈(コンテキスト)のよって変化する場合があり、このため「人間臭い言語」として知られている。たとえば、配列へ代入する場合はリストコンテキストで、スカラー変数へ代入する場合はスカラーコンテキストで評価される。一行ずつ読み出すか全体全てを読み出すかがコンテキストによって変わったりする。
以下のコードは、ファイルハンドルFILEから1行読み出して変数$lineに代入する。

$line = <FILE>;

以下のコードは、ファイルハンドルFILEからすべての行を読み込んで配列@textに代入する。

@text = <FILE>;

クロージャとしてのグローバル変数

Perlでは、グローバル変数を多用することで、オブジェクト指向を行わなくても、容易にデータの共有ができる。
これは、RubyJavaScriptにおける、クロージャと良く似ている。僕は、これを「グローバル変数による準クロージャ機能」と呼んで良いと思う。
Perlでは、グローバルスコープに変数や関数の宣言が置かれるため、どの関数からも同じ変数を参照することができて、簡単かつ、プログラミング初心者が自分のコードを書く上で、分かりやすい。
だが、これは小規模なスクリプト言語だからなせる業である。もっと大規模なプログラミングを行う上では、どの関数群がどの変数の宣言を共有するのか、きちんと分けて考えないといけない。そのためには、JavaC++のような、名前空間やパッケージ、そしてオブジェクト指向のクラスとそのメンバとインスタンスの考え方の導入が必要である。
これは、CとC++の関係にも言える。Cでは、さまざまな関数で同じ変数を共有するためには、グローバル変数を使うか、あるいは構造体のポインタを使う。プロトタイプ宣言はヘッダファイルに書かれている。だが、この方法では、開発者やその保守をする人が、プログラミングの実装の詳細について詳しく分かっていなければならない。JavaC++オブジェクト指向を使えば、それを隠蔽し、カプセル化することができる。

ラクダ本

Perlはプログラミング初心者に有用である。Perl作者のラリー・ウォールが書いた「プログラミングPerl」というラクダ本オライリーの表紙がラクダなのでそのように呼ばれている)は、今でもプログラミング言語のバイブル的存在である。

Perl 6 (Raku)

新しいPerlPerlの作者ラリー・ウォールが15年の歳月を経てリリースした伝説の新バージョン。
ラリー・ウォールは、Perl 6と呼ばれるPerlの後継バージョンを15年考えて開発した。最近リリースはされたが、本質的なところはPerl 5の雰囲気が残っている。Parrotと呼ばれる「軽量プログラミング言語のための共通仮想マシン」も作られていたが、それはどうなったのか定かではない。だが、Perl陣営は今でも頑張っている、ということは言えるだろう。
Perl 6の正式名称はRakuに決定した。これにより、Perl 6がPerl 5の後継バージョンではなく別の言語であることが明確になった。名称の由来は日本語の「楽」からである。僕の憶測だが、おそらく「ラクダ」からつけたかったのではないかと思う。ラクダは楽だ。

言語の基本

推奨されている記述

Perlでは、以下の二行を常に書くことが推奨されている。

use strict;
use warnings;

strictは潜在的な問題をコンパイル時に調べる。warningsは警告を表示する。

シングルクォーテーションとダブルクォーテーション

Perlでは、文字列を

'text'

とシングルクォーテーションで囲った場合、\'と\\を除いて変数や特殊文字の展開などがされないが、

"$value\n"

とダブルクォーテーションで囲った場合は、変数や特殊文字の展開がされる。
このため、変数の中身を表示したい時は

print "$value\n";

のように実行する。

算術演算子

Perlの算術演算子には、+, -, *, /, %, **などがあるほか、インクリメント・デクリメント演算子である++や--もある。

変数と代入

Perlでは変数の頭に$をつけます。

$price = 6000;
print $price * 1.1;

Perlでは数値や文字列のような単純なデータをスカラーデータと呼び、その変数をスカラー変数と呼ぶ。Perlにはスカラースカラー配列、スカラー連想配列という3つの基本的なデータ型があり、各データ型に対応した形で、それぞれスカラー変数、配列、連想配列(ハッシュ)の3つの変数が存在する。

配列とリスト

配列には@をつけます。

@score = (85, 60, 95);
print $score[0];

ハッシュ

ハッシュには%をつけます。

%hash = (
    '井上千佳子' => 35,
    '星野サトシ' => 22,
    'ミスターX' => 21,
    'ドナルド・シュバルツ' => 22,
);

foreach $key (keys(%hash)) {
    $value = $hash{$key};
    print "$key$value\n";
}

実行結果:

ドナルド・シュバルツ → 22
ミスターX → 21
井上千佳子 → 35
星野サトシ → 22

ハッシュの特徴として、「キーは重複しない」「値は重複しても良い」「順番は無意味」という特徴があります。
ここでは、順番が無意味なため、Perlが値を高速に検索するために都合の良い順番を勝手に決めてキーと値が表示されています。

push/pop/shift/unshift

pushとpopはスタックをあつかうための関数。pushは配列の最後に要素を付け加え、popは配列の最後から要素を取り出す。

push(@array, 'LAST_DATA');
$item = pop(@array);

また、shiftは配列の最初の要素を取り出し、unshiftは配列の最初に要素を追加する。
pushとshiftを使うことで、スタックではなくキューを実現できる。

splice

また、配列の途中から要素を抜き出したり、要素を挿入するには、spliceという関数を使う。

splice(対象配列, どこから削除するか, 削除する長さ, 挿入する別のリスト);

spliceを上手く使うことで、配列が楽に操作できる。

reverse/sort

reverseはリストを逆順にする関数。
sortはリストをソート(並び替え)する関数。

srandとrand

rand関数を使うことで、乱数(ランダムな値)を発生させることができる。
rand関数を使う前に、srand関数を使ってrand関数の初期化を行う。
以下は僕の書いたコード。rand()は浮動小数点数を返すため、int()で型を変換している。

for ($i = 0; $i < 10; $i++) {
  srand();
  $x = int(rand(2));
  if ($x == 0) {
    print "攻撃が命中した!\n";
  } elsif ($x == 1) {
    print "攻撃は外れた!\n";
  }
}

forとwhile

繰り返し。

for ($x = 1; $x <= 10; $x++) {
    print "x = $x\n";
}

whileを使って繰り返しを行うこともできる。

$x = 1;
while ($x < 10) {
    print "x = $x\n";
    $x++;
}

またuntil文やdo文を使うこともできる。次の繰り返しの前に何かを行いたい時はcontinueブロックに記述できる。リストの全要素に処理をかけたい時は、インデックスを用いずにforeach文を使う。

if ~ elsif ~ else

条件式。

if ($x > 5) {
    print "xは5より大きいです。\n";
} elsif ($x == 5) {
    print "xは5ちょうどです。\n";
} else {
    print "xは5より小さいです。\n";
}

unless文はif文と逆の意味で使える。

ファイル入力

ファイル入力。

open(IN, "data_file.txt");
while ($x = <IN>) { print $x; }
close(IN);

ファイル出力

ファイル出力。

open(OUT, "> data_file.txt");
print(OUT "メッセージ投稿1\n");
close(OUT);

標準出力

標準出力にschwarzと出力する:

print 'schwarz';

書き込みオープンしたファイルTXTにschwarzと出力する:

print TXT 'schwarz';

サブルーチン

サブルーチンには&をつけます。

sub add {
    return($_[0] + $_[1]);
}
print &add(13, 50);

Perlのサブルーチンでは、関数を呼び出した時に渡す引数は、サブルーチンの中では@_というリストに格納される。これを、$_[0]や$_[1]のようにアクセスできる。@_はサブルーチンの中のローカル変数で、参照はできるが代入はできない。与えられていない要素はundef(定義されていない)となる。
サブルーチンの&は省略することもできます。

サブルーチン2

@_は特殊な配列で、引数が格納されている。それぞれの引数には、$_[0]や$_[1]でアクセスできる。
my変数を用いると、その変数がサブルーチンの内部だけで使われるものであることを表すことができる。

sub calc_average2 {
    my ($x, $y) = @_;
    my $ave;
    $ave = ($x + $y) / 2;
    return $ave;
}

正規表現

$str = 'whoever';
if ($str =~ /who/) {
    print "マッチしました。\n";
else {
    print "マッチしませんでした。\n";
}

実行結果:

マッチしました。

$&は特殊な変数で、マッチした範囲を取り出すことができる。パターンマッチを行うと自動的に設定される。

$str = 'My age is 27.';
if ($str =~ /\d+/) {
    print "$& is the number.\n;
}

実行結果:

27 is the number.

以下は正規表現による置換の例:

$str = "Who is he.\n";
$str =~ s/he/she/;
print $str;

このように、s///構文を使うことで簡単に正規表現による置換ができる。また、パターンの末尾に

$str =~ s/he/she/g;

のように修飾子を付けることができる。

  • /g
    • 繰り返してパターンマッチを行う
  • /s
    • 文字列を一行として扱う(.が改行にもマッチする)
  • /m
    • 文字列を複数行として扱う(改行を区切りとした複数行として扱う)

また、tr///による置換を行うことで、大文字と小文字を変換したりすることができる。

$str =~ tr/A-Za-z/a-zA-Z/;

ほかにも、HTMLタグを消したい場合などは以下のような正規表現が使える。

$str =~ s/<[^>]*>//g;

joinとsplit

ちなみに、あるパターンで区切られている文字列をリストにしたい場合は、

@score = split(/,/, '90,95,65');
print "@score\n";

のようにする。これは,で区切られた文字列を複数の文字列にする。また、joinを使うことで、複数の文字列を1つに連結できる。

print join(':', @array);

splitに//を与えた場合、1文字(1バイト)ずつ切り出したリストを作ってくれる。このため、以下のようにできる。

$line = 'Hello, schwarz!';
@array = split(//, $line);
print join(':', @array);

実行結果:

H:e:l:l:o:,: :s:c:h:w:a:r:z:!

リファレンスの基本

リファレンスは、データに直接アクセスするのではなく、データの存在するメモリアドレスをスカラ値として定義する変数。
リファレンスを作るためには、変数に\をつける。リファレンスはスカラー変数となる。

@nums = (10, 20, 30);
$ref1 = \@nums;
$ref2 = $ref1;

$ref1と$ref2は同じ配列を指す。
配列のリファレンスを作成するためには以下のように[]を使うことができる。

$ref = [10, 20, 30];

デリファレンスを行うためには、@{配列のリファレンス}とする。リファレンスがスカラー変数の場合、{}を省略できる。

@nums = @$ref;

さまざまなリファレンス

配列ではなく、スカラー、ハッシュ、サブルーチンの場合は、

\$scalar
\%hash
\&sub

として定義し、デリファレンスするためには

$$scalar_ref
%$hash_ref
&$sub_ref(1, 2)

などとする。

オブジェクト

リファレンスとパッケージを組み合わせることにより、Perlオブジェクト指向プログラミングを行うことができる。
Perlでは、クラスは単なるパッケージであり、オブジェクトは単なるblessされたリファレンスであり、メソッドは第一引数にオブジェクトをとる単なるサブルーチンだが、これによって継承も委譲もできる驚くべき柔軟性を示す。見た目は変だが使いやすい。
オブジェクトを生成するにはbless関数を使う。

メソッドの第一引数

実際にはコンストラクタ、アクセサ、メソッドは第一引数にオブジェクトを取る。よって、

Hoge->new(meth => 'v1');

Hoge::new('Class', meth => 'v1');

を意味し、

$obj->meth;

Hoge::meth($obj);

を意味している。

簡単な計算プログラム

自分で作った簡単なプログラムです。攻撃力が10のキャラと、攻撃力が1だが「1ターンごとに攻撃力が1上がっていく」キャラが戦って、いつ上がっていく方のキャラが上回るか、を計算する。
正確にいうと、「いつ上回るかを計算する」というより、その「上回っていく過程」を表示する。
Rustで書いたコードをPerlに移植しました。

$x = 0;
$y = 0;
$count = 1;

while ($count <= 100) {
  $x += 10;
  $y += $count;
  print "$x\t$y\t$count\n";
  $count++;
}

簡単な説明

Perl

シジル

$はスカラー変数、@はリスト、%はハッシュ、&はサブルーチン

正規表現

「=~」で簡単に正規表現処理ができる

ファイル処理

UNIXに慣れ親しんだ人なら直観的に使いやすいファイル処理を行える