Perl入門ゼミ

テキスト処理、Linuxサーバー管理、Web開発ならPerl
  1. Perl
  2. here

現代的なPerlの記述方法一覧

Perl5.8以降における標準的なPerlの書き方を解説します。

インターネットで検索するとPerl4のころの古い記述がたくさんあります。また書籍などの多くもPerl4の記法で書かれています。Perl4の記法は複雑になりやすく間違いを生みやすいのでこれからPerlを書く人はPerl5の現代的な記法で記述することを強くお勧めします。

strictプラグマとwarningsプラグマ (必須)

strictプラグマとwarningsプラグマを有効にします。

use strict;
use warnings;

use strict;とuse warnings;の2行はスクリプトの最初に必ず記述してください。これらはPerlの文法チェックを厳しくするためのものです。面倒だという軽い気持ちでこれを記述しないと後々本当に面倒なことになります。

use strict;とuse warningsを書かなくてもよいのはワンライナーと呼ばれるコマンドラインスクリプトを記述するときだけだという風に考えてください。

ファイルハンドルにはレキシカル変数を使うこと (本当に特別な用途がない限りは必須)

ファイルハンドルには宣言したばかりのレキシカル変数を使います。レキシカル変数を宣言してそれをopen関数の第1引数に指定するとレキシカル変数にファイルハンドルが設定されます。

# レキシカル変数
my $fh;
my $file = 'file1';

open $fh, '<', $file
  or die "Cannot open '$file': $!";

while (my $lien = <$fh>) {
  ...
}

レキシカル変数はスコープを持つということと他の関数に引数として渡すことができるという大きな利点があります。古い解説にあるようなFHや*FHなどのシンボルを使わないようにします。

またレキシカル変数の宣言をopen関数の中にまとめてしまうのがより現代的であるといえるでしょう。

open my $fh, '<', $file

3引数のopen関数を使う (必須)

3引数のopen関数を使用するようにします。

open my $fh, '<', $file

古い解説では2引数のopen関数を解説しているものがありますが使わないようにしましょう。2引数のopen関数はセキュリティ的に脆いので使用してはいけません。

open my $fh, "< $file"; # 2引数のopen関数を使用してはいけない

これはパイプをオープンするときにも当てはまります。

# ○ (三引数)
open my $pipe, '-|', 'dir';

# × (二引数)
open my $pipe, 'dir |';

ファイルオープン時のエラー処理を行う (必須)

ファイルオープンを行ったときは必ずエラー処理を行うようにします。

open my $fh, '<', $file
  or die "Cannot open '$file': $!";

open関数は失敗するとundefを返すのでorを使ってエラー処理を行います。$!にはOSが返したエラーメッセージが含まれているのでユーザに見せるエラーメッセージに含めるようにします。

プログラムをエラーメッセージを表示して終了させるにはdie関数を使用します。他のプログラムから見た場合は終了コードは255になります。

ファイルオープンに限らずプログラムの外部と通信する場合はかならずエラー処理を行うようにします。外部というのはメモリの操作を除くすべてです。ファイルやネットワークなどが外部になります。

レキシカル変数とサブルーチンの名前には小文字とアンダーバーを使用する (強く推奨)

「myで宣言されるレキシカル変数」と「サブルーチン」の名前には小文字とアンダーバーを使用します。

# レキシカル変数
my $user_name;
my $search_word;
my $max_database_connection;

# サブルーチン
sub parse_data {
  ...
}

sub create_table {
  ...
}

この記法が良いか悪いかは別にしてCPANにある新しいモジュールのほとんどはこの記法で書かれています。この習慣にしたがっておいたほうがユーザに統一されたインターフェイスを提供できるという点で非常に多くの益があります。

変数の命名方法には別に記事を書いていますのでこちらも参考にしてください。

<変数に適切な名前をつける>

サブルーチンの命名方法は原則として「動詞 + 名詞」です。意味がはっきりとわかるものについてはユーザの利便性を考えて「動詞」だけにしても良いかもしれません。ただし後で困ることがあるので「動詞 + 名詞」にしておくのが無難だと思います。

パッケージ変数には大文字とアンダーバーを使用する (強く推奨)

パッケージ変数には大文字とアンダーバーを使用します。

our $OBJECT_COUNT;
our $CLASS_INFO;

パッケージ変数は使わずにレキシカル変数を使うようにする (必須)

もしあなたがモジュールの作者でないのであればパッケージ変数を使う機会はありません。もし単体のスクリプトの中でパッケージ変数を使っているとしたらそれは間違った使い方です。myを使ったレキシカル変数に変更しましょう。

標準的(?)なコードのフォーマットで書く (少し推奨)

コードの書き方には好き嫌いがあるのですが、Perlベストプラクティスで紹介されている書き方やPerltidyと呼ばれるコード整形ツールが出力する形式にあわせておいたほうが幾分よいと思います。

サンプルとしてMojo::URLのソースコードのリンクを張っておきます。これをまねして書けば覚えられます。

<Mojo::URLのソースコード>

まねするポイントを少しだけ書いておきます。

1. ifやforeach文やサブルーチンなどのスペースの入る位置を見る

# ifの直後にスペースがあって、()の中にはスペースがないなど
if ($flg) { 
  ...
}

2. コメントの書き方やスペースの入れ方などを見る

コメントの書き方や行のスペースの入れ方などをみましょう。CPANに存在するほとんどのモジュールには親切なコメントがないのですが、個人的は簡潔なコメントをソースコードに書いてくれるとありがたいと思っています。

3. タブは使わないでインデントの幅はスペースで4(あるいは2)

一応これはPerlベストプラクティスでいわれていることです。(最近の僕はスペース2です)。またタブを使いたいという人も中にはいると思いますので、これは個人の好みで。

日本語などのマルチバイト文字を適切に扱うためにEncodeモジュールを使用する (強く推奨)

日本語などのマルチバイト文字を適切に扱うにはEncodeモジュールを使用します。こちらは記事にしましたのでリンクを張っておきます。

<Encode 日本語などのマルチバイト文字列を適切に処理する>

古い解説にあるようなJcode.pmやJcode.plを使うような手法は現在では推奨できません。Perl5.8以降はEncodeモジュールを使用するのが標準的で問題が少ない方法です。

デフォルト変数 $_ は使用しない (強く推奨)

Perlにはデフォルト変数 $_ というものが存在します。デフォルト変数は関数に引数を指定しなかった場合に暗黙的に受け取る変数です。プログラムの中で使用すると可読性が落ちるので使うのは控えましょう。

デフォルト引数を使用するのは次の場合だけです。

1. ワンライナー

ワンライナーの中では使用してもよいと思います。printの引数や正規表現の対象として$_が利用されています。

# AAAという文字を含む行を取り出すワンライナー
perl -ne "print if /AAA/";

2. map関数とgrep関数と後置のfor

map関数やgrep関数や後置のforには$_ がわたってきますのでこれは利用せざるをえないです。

my @greped_array = grep { $_ =~ /AAA/ } @array;
my @mapped_array = map  { $_ * 2 } @array;
print $_ for @array;

有名なCPANモジュールの中にはデフォルト変数を使用しているものがありますが個人的には推奨しません。できるだけ明示的であったほうが可読性の高いプログラムになります。

foreach文ではレキシカル変数を宣言する (強く推奨)

Perl5ではforeach文の先頭でレキシカル変数を宣言することができます。

my @students = ('taro', 'kenji', 'naoya');
for my $student (@students) {
  ...
}

この例の場合は@studentsの各要素が$studentに入ってきます。$studentはレキシカル変数でforeachのブロックの先頭から終わりまでのスコープを持ちます。

レキシカル変数を省略するような書き方もできますが推奨しません。

# 推奨できない書き方
for (@students) {
  ...
}

コマンドライン引数の受け取りかた (参考)

コマンドライン引数はこんな感じで受け取るのがよいです。

# コマンドライン引数がひとつの場合
my $file = shift;
# コマンドライン引数が複数の場合
my ($file, $option) = @ARGV;

サブルーチンの引数の受け取り方 (参考)

コマンドライン引数の場合と同様になります。

# 引数がひとつの場合
sub func {
  my $file = shift;
}
# 引数が複数の場合
sub func {
  my ($file, $option) = @_;
}

日付処理の標準モジュールを使用する (参考)

もしPerl5.10以降を使用しているならTime::Pieceというモジュールが標準で添付されており日付処理に使えます。

<Time::Piece - 日付・時刻を扱うための標準モジュール>

またCPANからインストールできる環境であればDataTimeモジュールをインストールするのも良いかもしれません。こちらは高機能ですが少し重いです。

<日付を汎用的に扱うモジュール DateTime>

それもできないならlocaltimeやTime::Localでがんばります。

<Perlでの日付・時刻の扱い>

不必要なモジュールの読み込みは行わないこと (必須)

他のプログラムのソースコードをコピーしてきた場合にそのプログラムでは使用しないのに余計なモジュールが読み込まれている場合があります。これは後で読んだ人に対していらぬ誤解を招くので必ず削除するように心がけましょう。

# 他のプログラムからソースコードをコピーしてきたために
# 不必要なモジュールの読み込みが残る場合があるので
# 気をつける
use File::Spec;
use File::Basename 'basename'; 
                               

Perlのドキュメントの書き方 (参考)

仕事で使用する小さなスクリプトの場合はスクリプトの中にドキュメントを埋め込んでおくのがよいと思います。CPANモジュールの場合はソースコードの末尾がドキュメントになりますが、小さなスクリプトの場合は先頭に書いておくと利用者がソースコードを開いたときにぱっとみることができるので便利です。

PerlのドキュメントはPODと呼ばれる記法で書きます。簡単な書き方だけ紹介しておきます。「=head1」というのが見出しになります。「=head1」の右にタイトルを書きます。その下に一行あけて本文を書きます。一行あけるというのには意味があるので注意してください。ドキュメントの終わりは「=cut」という行になります。英語で書いた場合は次のようになります。

=head1 SCRIPT NAME

SomeScript.pl

=head1 DESCRIPTION

This script is used to do ....

=head1 USAGE

perl SomeScript.pl file1 file2 ...

=cut

# ソースコードの始まり
use strict;
use warnings;

ローカルなプロジェクトの場合は同僚にわかりやすく伝えるために日本語で書くのがよいと思います。

=head1 スクリプト名

SomeScript.pl

=head1 概要

~するためのものです。

=head1 使用方法

perl SomeScript.pl file1 file2 ...

=cut

# ソースコードの始まり
use strict;
use warnings;

コメントの#の嵐は避ける (推奨)

よく#だらけのコメントを仕事をしててみるのですが個人的にはお勧めしません。一番の理由は一度そのコメントの記述を行うと後から来た人がそれをまねしないといけないからです。関数ひとつ記述するのにこれをまねしないといけないのかと思うと気持ちが萎えます。またコードの品質を上げるどころか関数を書き換えたときにコメントが追いついていないということが頻繁に起きます。ですのでやめましょう。

#################################################################
# 関数名     : ほにゃらら                                       #
# 引数       : 引数1 引数2                                      #
# 戻り値     : ほにゃらら                                       #
# 作成日時   : あああああ                                       #
# 作成者     : ほれほれ                                         #
# 関数の説明 : いいいいいい                                     #
# 更新履歴   : その1                                            #
#            : その2                                            #
#            : その3                                            #
#################################################################
sub func {
    
}

こちらの書き方をお勧めします。

# 簡単な解説(1行で)
sub func{
    
}

<Mojo::URLのソースコード>

も参考にしてください。

文字列リスト演算子 (参考)

文字列リスト演算子はよく使用されるので解説しておきます。文字列リスト演算子は文字列の配列を作成するのによく利用されれます。

my @strings = qw/aa bb cc/;

次の記述と同じ意味があります。

my @strings = ('aa', 'bb', 'cc');

モジュールの関数をインポートするときは明示する (強く推奨)

モジュールで関数をインポートするときは明示するようにしたほうがよいでしょう。ソースコードを読んだ人がその関数はどのモジュールのものなのかを簡単に理解することができます。

use File::Basename 'basename';
use File::Copy qw/copy move/;
use File::Path 'mkpath';
use Encode qw/encode decode/;

# mkpath関数を使用する。
mkpath $dir;

もし明示的なインポートの記述がなかった場合はどうなるでしょう。

use File::Basename;
use File::Copy;
use File::Path;
use Encode;

# これはどこからインポートされたかわからない 
mkpath $dir; 

このような場合はuseされているすべてのモジュールのドキュメントを読むということになりかねません。あなたは関数がどのモジュールからインポートされたのかを知っているかもしれませんが、ソースコードを読む人には明示的ではありません。ですのでインポートする関数はどんなにあなたにとって明らかに思えても明示的に指定するようにしましょう。

(参考)File::PathFile::Copy

gotoは使用しない (本当に特別な場合を除いて必須)

Perlにはgoto文がありますが使用してはいけません。gotoを使うプログラミングはPerlに限らずもう過去のものです。もしあなたが何らかの理由でgotoを使いたくなった場合代替する手段は必ず用意されていると思ってください。

ループ制御を行いたいなら「last」「next」を使用してください。エラー処理を行いたいならdieを使って例外を投げてください。

(gotoを使用しなければならない場面は、無限再帰呼び出しなどで、関数の階層を深くしたくないなどという本当に特殊な場合だけです。)

do ~ whileは使用しない (推奨)

do while文で記述できる文はwhile文で必ず記述できます。do while文を使ったからといって記述が簡潔になるかといえばそうでもないです。逆に普段使用していない分だけ意図がわかりにくくなると感じます。

do while文で記述できる文はwhile文で必ず記述できるのでwhile文を使うロジックを考えることをお勧めします。

redoは使用しない (推奨)

Perlにはredo文がありますが、redoを使わなくても同じロジックを記述することができます。redoは何回か使用したことがあるのですが、redoを使ったプログラミングはとてもわかりにくくなると感じます。redoを使用しなくても同じロジックは必ず記述できるのでredoを使わないロジックを考えることをお勧めします。

プロトタイプは使用しない (強く推奨)

サブルーチンを定義するときにプロトタイプという型を指定できる機能がありますがこれは使用しません。

# プロトタイプは使用しないこと
sub func ($@) {
  ...
}

Perlでは明示的に型を指定しなくてもどのような型の引数も受け取れますし、引数の個数もいくつでもかまいません。ですのでプロトタイプで型を指定したり個数を指定したりする必要はまったくないのです。ですので必ずプロトタイプを指定しないサブルーチンの定義を行いましょう。

sub func {
  ...    
}

エラーを伝えるときはundefを戻り値として返却するのではなくdieを使用する。(推奨)

Perlには例外処理がないと思っている人もいるかもしれません。Javaのような例外オブジェクトというものはありませんが、簡潔な例外機構を備えています。

まずは旧来のエラー処理であった戻り値にundefを返却する方法を見ます。エラーが発生したときに単独のreturnを記述するとスカラコンテキストの場合はundefがリストコンテキストの場合は空のリスト () が返却されます。

# エラーが発生したときにundefを返却する
sub func {
  my $arg = shift;
  
  ...
  
  # エラー処理
  if ($error) {
    return; 
  }
  # エラーが起こらなかった場合の正しい値
  return $val;
}

そして関数を呼び出す側でエラー処理を記述します。

my $val = func();

# $valが偽値だったらプログラムを終了
die "Error" unless $val;

この記述の問題点はfuncを使う人が戻り値のチェックを怠るとプログラムは先に進んでしまうということです。

ですので現在的なPerlではエラーを伝えるときにdieを使って例外を投げます。

# エラーが発生したときにdieを使って例外を投げる
sub func {
  my $arg = shift;
  
  # なんらかの処理
  
  # dieを使って例外を投げる
  if ($error) {
    die "Error message";
  }

  # エラーが起こらなかった場合の正しい値
  return $val;
}

このようにするとfuncを呼び出してエラーが発生したときはエラーメッセージを表示してプログラムは終了します。

# エラーが発生した場合はエラーメッセージを表示してプログラムが終了
func(); 

プログラムを終了させたくない場合はevalブロックで受けます。これはJavaでいうcatchだと思ってください。エラーが発生した場合は$@という特殊変数にエラーの内容が設定されますので、この変数をチェックすることでエラーが発生したかどうかを調べることができます。

eval { func() };

if ($@) {
  # エラーが発生した場合の処理を記述
}

コンストラクタの呼び出しではアロー演算子を利用する

コンストラクタの呼び出しはアロー演算子を使用するのがよいでしょう。Perlではコンストラクタと他のメソッドに実質的な違いはありません。

my $obj = SomeClass->new;

間接オブジェクト呼び出しは、将来的には非推奨になる可能性があります。

# 間接オブジェクト呼び出し
my $obj = new SomeClass();