Perlによるプログラミングに関する世界観です。Perl/CGIも参照のこと。
とほほのperl入門、Perl言語プログラミングレッスン 入門編を参考にしています。
Perlは、インタープリタ式のスクリプト言語で、プログラミング入門者から熟練のプログラマまで、多くのプログラマに広く使われている言語です。
実際のところ、「Perlを一度触るとプログラミングの基本が分かる」ということが言えます。
プログラミングを行う上で、プログラミングとはどのようなものなのか、どのようにコードを書いて実行するのか、ということが分かりやすいため、「入門者向き」と言えます。
PerlはUNIXのフィルターコマンドやシェルスクリプトやsedコマンドなどの考え方をベースとしているため、標準入出力やファイル処理など、Perlの教科書にはUNIXの用語が飛び交うこともしばしばです。そのため、UNIXの基本的な知識を知っていれば、習得の助けとなります。
また、PerlはUNIXのフィルターコマンドと同様に、パイプなどで他のUNIXコマンドやシェルスクリプトと組み合わせて、Perlスクリプトを使えます。このため、「このコマンドで実行した結果を簡単に再処理したい」といった場合に、Perlで簡単なスクリプトを書いてその中に「はめこむ」といった使い方をすることができ、システム管理に役立ちます。
C/C++言語などと違い、Perlには変数の型指定がなく、型はその場その場で動的に決まります。ですから、
$s = ("100" . "000") x 2;
として"100000100000"という文字列を作った後で、それを
$s += 500;
などとし、「数値を文字列にし、その上でまた数値にする」といったことができます。
Perlは正規表現も強力で、「リスト@listの中のisという文字列が含まれている要素を表示する」のようなことをパターンマッチングで処理することができます。
正規表現は、たとえばログファイルやUNIXの設定ファイルのように、「ある一定の形式で書かれているテキストを整形する」場合に役に立ちます。これはUNIXでは多くの場合タブや空白で区切られており、場合によってはawkやsedなどでも処理できるところを、より高度にPerlで行うこともできます。
PerlはUNIXと親和性が高く、UNIXシステムでは最初からインストールされていることがほとんどですが、Windowsからも使えます。ActiveState社のActivePerlとStrawberry Perlがあり、僕個人の意見ではStrawberry Perlがおすすめです。ActivePerlのように導入のためにアカウントを作成する必要が無く、msiファイルだけをダウンロードして実行することで簡単に導入でき、必要なバイナリはgccなども含めて全てインストールされるため、CPANのあらゆるモジュールを利用できます。僕もWindowsにおけるPerlはStrawberry Perlを使っています。
Perlについて。
Wikipedia
ソースコード
Perl言語の入門。
PerlはWebサーバーのCGIとして実行されることが多いです。
Perlでは、常に以下の二行を書くことが推奨されている。
use strict; use warnings;
これにより、strictとwarningsの厳密なエラー・警告がONになる。
Perlでは、文字列を
'hoge'
とシングルクォーテーションで囲った場合、\'と\\を除いて変数や特殊文字の展開などがされないが、
"$nameさん\n"
とダブルクォーテーションで囲った場合は、変数や特殊文字の展開がされる。
このため、変数の中身を表示したい時は
print "$nameさん\n";
のように実行する。
Perlの算術演算子には、+, -, *, /, %, **などがあるほか、インクリメント・デクリメント演算子である++や--もある。
また、文字列用の演算子として、文字列を連結する.などがある。
Perlでは、一行コメントの#が利用できる。行の中の#に続く文字列は行末まで無視される。複数行コメントは存在しない。
2023.02.03
Perlでは変数の頭に$をつけます。
$x = 100; $y = 200; $sum = $x + $y; print "$sum\n";
Perlでは数値や文字列のような単純なデータをスカラーデータと呼び、その変数をスカラー変数と呼ぶ。Perlにはスカラー変数、配列、連想配列(ハッシュ)の3つの変数が存在する。
配列(リスト)には@をつけます。
@array = (1..100); foreach $item (@array) { print "$item\n"; }
1..4のように書くと(1, 2, 3, 4)というリストを表す。また@array[1, 2, 3]と書くとリストの2, 3, 4番目の要素からなるスライスを作ることができる。添え字は0から始まり、$list[0]は一番目の要素を表す。逆からアクセスする時は$list[-1]などのように-1から始まる。
配列とリストとハッシュも参照のこと。
ハッシュには%をつけます。
%player = ('HP' => 100, 'ATK' => 200, 'DEF' => 50); %enemy = ('HP' => 610, 'ATK' => 70, 'DEF' => 10); while ($enemy{'HP'} > 0 && $player{'HP'} > 0) { $enemy{'HP'} = $enemy{'HP'} - ($player{'ATK'} - $enemy{'DEF'} ); $player{'HP'} = $player{'HP'} - ($enemy{'ATK'} - $player{'DEF'}); print "プレイヤーの残りHP: $player{'HP'}\n"; print "敵の残りHP: $enemy{'HP'}\n"; } if ($player{'HP'} <= 0) { print "プレイヤーの敗北\n"; } else { print "プレイヤーの勝利\n"; }
リストでは[]を添え字に使うが、ハッシュでは{}を添え字に使う。@arrayの最初の要素は$array[0]となり、%hashのキーschwarzの値は$hash{'schwarz'}となる。値は重複してもよいがキーは重複できないため、同じキーに対する値の代入は追加ではなく上書きになる。
後日注記:上のゲームのプログラムにはバグがあります。プレイヤーのDEFよりも敵のATKが低い場合、数値がマイナスとなってそれを減算しているため、プレイヤーのHPが上がってしまいます。上のプログラムは簡単なサンプルであるため、簡素さを重視してそのままで掲載しています。本当にゲームプログラミングをするつもりなら、そういうところを自分の力で直すと良いでしょう。
ゲーム開発も参照のこと。
push()とpop()はスタックをあつかうための関数。push()は配列の最後の位置に新しい要素を追加し、pop()は配列の最後の位置にある要素を取り出して削除する。
@names = ('Assy', 'Schwarz'); push(@names, 'Zaidou'); $name = pop(@names);
また、shift()は配列の最初の位置にある要素を取り出して削除し、unshift()は配列の最初の位置に新しい要素を追加する。要素はひとつひとつシフト(ずらす)される。
push()とshift()を使うことで、スタックではなくキューを実現できる。
スタックとキューも参照のこと。
また、配列の途中から要素を抜き出したり、要素を挿入するには、splice()という関数を使う。
splice(配列, 削除を開始する位置, 削除する要素の数, 挿入する別のリスト);
splice()を上手く使うことで、配列が楽に操作できる。
reverse()はリストを逆順にする関数。
@array = (1, 2, 3, 4, 5); @rev = reverse(@array);
sort()はリストをソート(並び替え)する関数。
@names = ('Schwarz', 'Zaidou', 'Assy'); @sorted = sort(@names);
リストを逆順でソートしたい場合は、sort()したリストに対してさらにreverse()をかける。reverse(sort(@list))のようになる。
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"; } }
ゲーム開発も参照のこと。
構造化プログラミングも参照のこと。
条件式はif ~ elsif ~ elseで行う。
<STDIN>とともに用いれば、入力に対して条件判断を行える。
print "物理攻撃は1, 魔法攻撃は2, 逃げるは3を入力してください。\n"; $line = <STDIN>; if ($line == 1) { print "$nameは物理攻撃を行った!\n"; } elsif ($line == 2) { print "$nameは魔法攻撃を行った!\n"; } elsif ($line == 3) { print "$nameは逃亡した!\n"; }
以下のようにすれば数値ではなく文字列として比較できる。比較の前に、改行文字を削除するためにchomp()が必要。
print "物理攻撃はA, 魔法攻撃はB, 逃げるはCを入力してください。\n"; $line = <STDIN>; chomp($line); if ($line eq "A") { print "$nameは物理攻撃を行った!\n"; } elsif ($line eq "B") { print "$nameは魔法攻撃を行った!\n"; } elsif ($line eq "C") { print "$nameは逃亡した!\n"; }
unless文はif文と逆の意味で使える。
条件式は==(等値)が=(代入)とは別の演算子であることに注意しよう。また条件式の範囲に注意。$x >= 3と$x > 3はまったく範囲が異なる。
繰り返しは、C言語と同じfor文を用いる。
for ($i = 0; $i < 10; $i++) { print (($i + 1) . "回目のターンです。\n"); }
while文を使って繰り返しを行うこともできる。while()の括弧の中には条件式を記述する。以下は上のfor文と同等のwhile文。
$i = 0; while ($i < 10) { print (($i + 1) . "回目のターンです。\n"); $i++; }
またuntil文やdo文を使うこともできる。次の繰り返しの前に何かを行いたい時はcontinueブロックに記述できる。
リストの全要素に処理をかけたい時は、インデックスを用いずにforeach文を使う。
foreach文はリストに対してひとつひとつの要素に繰り返し処理を行うことができる。また、ファイルハンドルを使うことでファイルの各行に処理を行うこともできる。
foreach文のひとつひとつのインデックス変数は省略した場合$_からアクセスできる。またインデックス変数は複製ではなく別名であるため、要素を書き換えて代入するためにも使える。
@names = ('Assy', 'Schwarz', 'Zaidou'); foreach $name (@names) { $name = $name . "さん"; }
C言語におけるbreakはlast、continueはnextが同等の文に相当する。
リストの全てに処理を行って別のリストを作りたい時はmap()関数を使うのが便利。
@names = ('Assy', 'Schwarz', 'Zaidou'); @sans = map { $_ . "さん" } @names;
Perlではデフォルト引数の$_を使うことでmap()に与えるリストの各要素を指定できる。
ファイル入力。
print "<p>掲示板のログ: $thread\n"; open(IN, "data_$thread.txt") or die "$!"; while ($line = <IN>) { print "<br>$line\n"; } close(IN); print "</p>\n";
Perlでは本来そこにリストが記述されるべき場所に<FILE>と記述されていると、対応するファイルハンドルから全部の行を読み込む。またスカラーコンテキストで$x = <IN>と記述されているとファイルから一行読み込む。ファイルハンドルはopen()で開き、close()で閉じる。
ファイルではなくディレクトリを扱う場合は、opendir(), readdir(), closedir()などとする。ファイル一覧の取得にはglob()関数も便利。
$x = <STDIN>とすることで、標準入力から一行読み込める。代入されるコマンドライン入力行には改行が含まれているので、chomp()関数を使って改行を取り除く。
後日注記:or dieはエラー処理。ファイルが存在しなかった場合などに呼ばれる。
ファイル出力。
open(OUT, ">> data_$thread.txt") or die "$!"; print(OUT "$usernameさんのメッセージ:\n"); print(OUT "$comment"); print(OUT "\n----\n"); close(OUT);
掲示板を作る時などは、スレッドを示すファイルにどんどん投稿のHTMLを追記していけば簡単に作れる。
後日注記:ファイル名に>を付加するだけで、書き込みオープンが実現できる。>>は追記を表す。
掲示板開発も参照のこと。
Linuxシステムコール・APIも参照のこと。
サブルーチンには&をつけます。
Perlのサブルーチンでは、関数を呼び出した時に渡す引数は、サブルーチンの中では@_というリストに格納される。これを、$_[0]や$_[1]のようにアクセスできる。@_はサブルーチンの中のローカル変数で、参照はできるが代入はできない。与えられていない要素はundef(定義されていない)となる。
サブルーチンの&は省略することもできます。
my変数を用いると、その変数がサブルーチンの内部だけで使われるものであることを表すことができる。
sub calc { my ($a, $b) = @_; my $add = $a + $b; my $sub = $a - $b; my $mul = $a * $b; my $div = $a / $b; print "$a + $b = $add\n"; print "$a - $b = $sub\n"; print "$a * $b = $mul\n"; print "$a / $b = $div\n"; } calc(2, 20); calc(5, 3); calc(12, 4);
関数も参照のこと。
Perlのサブルーチンでは、引数を@_で受け取り、$_[0]のようにアクセスする。
これは、Perlのサブルーチンが可変長引数、すなわち引数を何個得るか分からないような関数呼び出しを想定している、ということから設計されている。
C言語では、関数の引数の個数はあらかじめ決まっている。例外はprintf()などで、そのような場合は無理やり可変長引数を実装している。
Perlのサブルーチンは、最初から可変長引数をベースに設計されており、引数が何個あるか分からないようなサブルーチンの呼び出しであっても上手く実行されるようにできている。
(詳しくはプログラミングPerl 改訂版が参考になります。)
別のファイルのサブルーチンを使うには、requireを使う。requireでファイルを読み込むと、ファイルの中に記述されたサブルーチンが使えるようになる。
モジュールを使う時はuseを使う。
Perlは強力な正規表現が使えることで有名。
Perl言語プログラミングレッスン 入門編を参考に執筆しました。
$str = 'hooooge'; if ($str =~ /ho*ge/) { print "マッチしました。\n"; else { print "マッチしませんでした。\n"; }
実行結果:
マッチしました。
$&は特殊な変数で、マッチした範囲を取り出すことができる。パターンマッチを行うと自動的に設定される。
$str = 'Hoge is 23 years old.'; if ($str =~ /\d+/) { print "Age is $&.\n"; }
実行結果:
Age is 23.
以下は正規表現による置換の例:
$str = "Who is Hoge?\n"; $str =~ s/Hoge/he/; print $str;
このように、s///構文を使うことで簡単に正規表現による置換ができる。また、パターンの末尾に
$str =~ s/Hoge/he/g;
のように修飾子を付けることができる。
修飾子 | 意味 |
---|---|
/g | 繰り返してすべてのマッチする場所にパターンマッチを行う |
/s | 文字列を1行のように操作する(.が改行にもマッチする) |
/m | 文字列を複数行のように操作する(改行ごとに^や$がマッチする) |
また、tr///による置換を行うことで、小文字a-zを大文字A-Zに変換したりすることができる。
$str =~ tr/a-z/A-Z/;
ほかにも、HTMLタグを消したい場合などは以下のような正規表現が使える。
$str =~ s/<[^>]*>//g;
Perlではデフォルト引数$_を上手く使うことで、まったく変数を使わなくても、「それに対してそれを処理する」ような形で処理を行うこともできる。たとえば、
while (<STDIN>) { if(/^map/) { print; } }
などとできる。
パターンを()でかこって、$1や$2などから複数のマッチした場所を後で使うこともできる。パターンマッチの中で置換する場合などは$1ではなく\1を使うことに注意。
正規表現については正規表現のページを参照のこと。
Perl言語プログラミングレッスン 入門編を参考に執筆しました。
ちなみに、あるパターンで区切られている文字列をリストにしたい場合は、
@score = split(/,/, '70,95,50'); print "@score\n";
のようにする。これは,で区切られた文字列を複数の文字列にする。また、join()を使うことで、複数の文字列を1つに連結できる。
print join(':', @array);
split()に//を与えた場合、1文字ずつ分割してリストを作れる。このため、以下のようにできる。
$line = 'Hoge, schwarz!'; @array = split(//, $line); print join('-', @array);
実行結果:
H-o-g-e-,- -s-c-h-w-a-r-z-!
リファレンスは、C言語のポインタと同様の参照型のスカラー変数。
リファレンスを作るためには、変数名の頭に\をつける。
@array1 = (14, 23, 3, 5); $ref_array1 = \@array1; $ref_array2 = $ref_array1;
リファレンスはスカラー変数となるため$を付ける。$ref_array1と$ref_array2は同じ配列@array1を指す。
配列のリファレンスの場合、[]を使って以下のように記述できる。
$ref_array3 = [14, 23, 3, 5];
配列のリファレンスに対して、デリファレンスを行うためには、リファレンスの変数名に対して@を頭に付ける。たとえば@$ref_array3となる。
@val_array1 = @$ref_array3;
また、配列のリファレンスから要素を取得するには、->を使うこともできる。
$item1 = $ref_array3->[0];
リストの中にスカラー値しか格納することのできないPerlでは、リストのままでは多次元配列を作ることができない。スカラー値として表現されるリファレンスを用いれば、Perlでも多次元配列を使うことができる。
$ref_array4 = [ [1, 2, 3], [4, 5, 6] ]; $item2 = $ref_array4->[0]->[0];
2023.02.03編集
配列ではなく、スカラー、ハッシュ、サブルーチンのリファレンスは以下のようになる。
$x = 15; %y = ( hoge => "Hoge", fuga => "Fuga" ); sub sub_hoge { print "Hoge\n"; } $ref_sc1 = \$x; $ref_hs1 = \%y; $ref_sb1 = \&sub_hoge;
としてそれぞれのリファレンスを得られる。これらをデリファレンスするためには
$val_sc1 = $$ref_sc1; %val_hs1 = %$ref_hs1; &$ref_sb1;
などとする。
ハッシュのリファレンスは、以下のようにすることでキーから値を取得することもできる。
$hoge = $ref_hs1->{"hoge"};
また、サブルーチンのリファレンスを実行するには以下のようにすることもできる。
$ref_sb1->();
2023.02.03編集
リファレンスとパッケージを組み合わせることにより、Perlでオブジェクト指向プログラミングを行うことができる。
オブジェクトのインスタンスは、コンストラクタの中で、bless()関数で作り出す。そしてコンストラクタとメソッドは、第一引数にそのオブジェクトへのリファレンスを取る。
たとえば以下のようなコードがあったとして、
$cat = Cat->new(); $cat->mew();
とした時、Cat::mew()の第一引数が$catとなる(要するにCat::mew($cat)となる。)
このCatは以下のように実装する。
package Cat; sub new { my $class = shift; my $self = { hello => 'mew', @_, }; return bless $self, $class; } sub mew { my $self = shift; print $self->{hello}; } 1
オブジェクト指向も参照のこと。
以下のサイトでは、Object::Simpleというモジュールを用いて、オブジェクト指向を実現している。
system()関数を使うことで、Linuxのシェル入力のように新しいプロセスを実行できる。
後日注記:プログラムの実行後の出力を得たい時は、バッククォートを使って`~`とする。
eval()を使うことで、文字列の格納された変数をPerlの文として実行できる。
Perlの文法は文脈(コンテキスト)のよって変化する場合があり、このため「人間臭い言語」として知られている。たとえば、配列へ代入する場合はリストコンテキストで、スカラー変数へ代入する場合はスカラーコンテキストで評価される。一行ずつ読み出すか全体全てを読み出すかがコンテキストによって変わったりする。
次の場合、ファイルハンドルHOGEから読み出されて$lineに代入されるのは一行だけ。
$line = <HOGE>;
これをループすることで、一行ずつ読み出して処理することができる。
次の場合、HOGEからすべての行が一気に読み出されて@bufに代入される。
@buf = <HOGE>;
後日注記:Perlには型がないが、変数が数値として扱われるか文字列として扱われるかはコンテキストによって決定する。
(Perl言語プログラミングレッスン 入門編を参考に執筆しました。)
ヒアドキュメントは、長い文章を表示する際に便利な機能で、キーワードまでの文字列を一気にprintしたりできる。
$header = <<"EndHeader"; <!DOCTYPE html> <html dir="ltr"> <head> <title>$title</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> </head> <body> EndHeader $footer = <<"EndFooter"; </body> </html> EndFooter print $header; hogehoge(); print $footer;
Perlのライブラリやモジュール、Perlで書かれたソフトウェアのアーカイブ。
Perl(戯言)を参照のこと。
Perl/CGIを参照のこと。
以下の書籍・ページが参考になります。
自分で作った簡単なプログラムです。攻撃力が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++; }
$はスカラー変数、@はリスト、%はハッシュ、&はサブルーチン。
「=~」で簡単に正規表現処理ができる。
UNIXに慣れ親しんだ人なら直観的に使いやすいファイル処理を行える。
Perl
Perl
書籍