この広告は、90日以上更新していないブログに表示しています。
Perl5.8以降における標準的なPerlの書き方を解説します。
インターネットで検索するとPerl4のころの古い記述がたくさんあります。また書籍などの多くもPerl4の記法で書かれています。Perl4の記法は複雑になりやすく間違いを生みやすいのでこれからPerlを書く人はPerl5の現代的な記法で記述することを強くお勧めします。
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,'<',$fileordie"Cannot open '$file':$!";while (my$lien = <$fh>) { ...}
レキシカル変数はスコープを持つということと他の関数に引数として渡すことができるという大きな利点があります。古い解説にあるようなFHや*FHなどのシンボルを使わないようにします。
またレキシカル変数の宣言をopen関数の中にまとめてしまうのがより現代的であるといえるでしょう。
openmy$fh,'<',$file
3引数のopen関数を使用するようにします。
openmy$fh,'<',$file
古い解説では2引数のopen関数を解説しているものがありますが使わないようにしましょう。2引数のopen関数はセキュリティ的に脆いので使用してはいけません。
openmy$fh,"<$file";# 2引数のopen関数を使用してはいけない
これはパイプをオープンするときにも当てはまります。
# ○ (三引数)openmy$pipe,'-|','dir';# × (二引数)openmy$pipe,'dir |';
ファイルオープンを行ったときは必ずエラー処理を行うようにします。
openmy$fh,'<',$fileordie"Cannot open '$file':$!";
open関数は失敗するとundefを返すのでorを使ってエラー処理を行います。$!にはOSが返したエラーメッセージが含まれているのでユーザに見せるエラーメッセージに含めるようにします。
プログラムをエラーメッセージを表示して終了させるにはdie関数を使用します。他のプログラムから見た場合は終了コードは255になります。
ファイルオープンに限らずプログラムの外部と通信する場合はかならずエラー処理を行うようにします。外部というのはメモリの操作を除くすべてです。ファイルやネットワークなどが外部になります。
「myで宣言されるレキシカル変数」と「サブルーチン」の名前には小文字とアンダーバーを使用します。
# レキシカル変数my$user_name;my$search_word;my$max_database_connection;# サブルーチンsubparse_data{ ...}subcreate_table{ ...}
この記法が良いか悪いかは別にしてCPANにある新しいモジュールのほとんどはこの記法で書かれています。この習慣にしたがっておいたほうがユーザに統一されたインターフェイスを提供できるという点で非常に多くの益があります。
変数の命名方法には別に記事を書いていますのでこちらも参考にしてください。
<変数に適切な名前をつける>
サブルーチンの命名方法は原則として「動詞 + 名詞」です。意味がはっきりとわかるものについてはユーザの利便性を考えて「動詞」だけにしても良いかもしれません。ただし後で困ることがあるので「動詞 + 名詞」にしておくのが無難だと思います。
パッケージ変数には大文字とアンダーバーを使用します。
our$OBJECT_COUNT;our$CLASS_INFO;
もしあなたがモジュールの作者でないのであればパッケージ変数を使う機会はありません。もし単体のスクリプトの中でパッケージ変数を使っているとしたらそれは間違った使い方です。myを使ったレキシカル変数に変更しましょう。
コードの書き方には好き嫌いがあるのですが、Perlベストプラクティスで紹介されている書き方やPerltidyと呼ばれるコード整形ツールが出力する形式にあわせておいたほうが幾分よいと思います。
サンプルとしてMojo::URLのソースコードのリンクを張っておきます。これをまねして書けば覚えられます。
まねするポイントを少しだけ書いておきます。
# ifの直後にスペースがあって、()の中にはスペースがないなどif ($flg) { ...}
日本語などのマルチバイト文字を適切に扱うにはEncodeモジュールを使用します。こちらは記事にしましたのでリンクを張っておきます。
<Encode 日本語などのマルチバイト文字列を適切に処理する>
古い解説にあるようなJcode.pmやJcode.plを使うような手法は現在では推奨できません。Perl5.8以降はEncodeモジュールを使用するのが標準的で問題が少ない方法です。
Perlにはデフォルト変数 $_ というものが存在します。デフォルト変数は関数に引数を指定しなかった場合に暗黙的に受け取る変数です。プログラムの中で使用すると可読性が落ちるので使うのは控えましょう。
デフォルト引数を使用するのは次の場合だけです。
Perl5ではforeach文の先頭でレキシカル変数を宣言することができます。
my@students = ('taro','kenji','naoya');formy$student (@students) { ...}
この例の場合は@studentsの各要素が$studentに入ってきます。$studentはレキシカル変数でforeachのブロックの先頭から終わりまでのスコープを持ちます。
レキシカル変数を省略するような書き方もできますが推奨しません。
# 推奨できない書き方for (@students) { ...}
コマンドライン引数はこんな感じで受け取るのがよいです。
# コマンドライン引数がひとつの場合my$file =shift;
# コマンドライン引数が複数の場合my ($file,$option) =@ARGV;
コマンドライン引数の場合と同様になります。
# 引数がひとつの場合subfunc{my$file =shift;}
# 引数が複数の場合subfunc{my ($file,$option) =@_;}
もしPerl5.10以降を使用しているならTime::Pieceというモジュールが標準で添付されており日付処理に使えます。
<Time::Piece - 日付・時刻を扱うための標準モジュール>
またCPANからインストールできる環境であればDataTimeモジュールをインストールするのも良いかもしれません。こちらは高機能ですが少し重いです。
それもできないならlocaltimeやTime::Localでがんばります。
他のプログラムのソースコードをコピーしてきた場合にそのプログラムでは使用しないのに余計なモジュールが読み込まれている場合があります。これは後で読んだ人に対していらぬ誤解を招くので必ず削除するように心がけましょう。
# 他のプログラムからソースコードをコピーしてきたために# 不必要なモジュールの読み込みが残る場合があるので# 気をつけるuseFile::Spec;useFile::Basename'basename';
仕事で使用する小さなスクリプトの場合はスクリプトの中にドキュメントを埋め込んでおくのがよいと思います。CPANモジュールの場合はソースコードの末尾がドキュメントになりますが、小さなスクリプトの場合は先頭に書いておくと利用者がソースコードを開いたときにぱっとみることができるので便利です。
PerlのドキュメントはPODと呼ばれる記法で書きます。簡単な書き方だけ紹介しておきます。「=head1」というのが見出しになります。「=head1」の右にタイトルを書きます。その下に一行あけて本文を書きます。一行あけるというのには意味があるので注意してください。ドキュメントの終わりは「=cut」という行になります。英語で書いた場合は次のようになります。
=head1 SCRIPT NAMESomeScript.pl=head1 DESCRIPTIONThis script is used to do ....=head1 USAGEperl 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 ##################################################################subfunc{ }
こちらの書き方をお勧めします。
# 簡単な解説(1行で)sub func{ }
<Mojo::URLのソースコード>
も参考にしてください。
文字列リスト演算子はよく使用されるので解説しておきます。文字列リスト演算子は文字列の配列を作成するのによく利用されれます。
my@strings =qw/aa bb cc/;
次の記述と同じ意味があります。
my@strings = ('aa','bb','cc');
モジュールで関数をインポートするときは明示するようにしたほうがよいでしょう。ソースコードを読んだ人がその関数はどのモジュールのものなのかを簡単に理解することができます。
useFile::Basename'basename';useFile::Copyqw/copy move/;useFile::Path'mkpath';useEncodeqw/encode decode/;# mkpath関数を使用する。mkpath$dir;
もし明示的なインポートの記述がなかった場合はどうなるでしょう。
useFile::Basename;useFile::Copy;useFile::Path;useEncode;# これはどこからインポートされたかわからないmkpath$dir;
このような場合はuseされているすべてのモジュールのドキュメントを読むということになりかねません。あなたは関数がどのモジュールからインポートされたのかを知っているかもしれませんが、ソースコードを読む人には明示的ではありません。ですのでインポートする関数はどんなにあなたにとって明らかに思えても明示的に指定するようにしましょう。
(参考)File::Path、File::Copy
Perlにはgoto文がありますが使用してはいけません。gotoを使うプログラミングはPerlに限らずもう過去のものです。もしあなたが何らかの理由でgotoを使いたくなった場合代替する手段は必ず用意されていると思ってください。
ループ制御を行いたいなら「last」「next」を使用してください。エラー処理を行いたいならdieを使って例外を投げてください。
(gotoを使用しなければならない場面は、無限再帰呼び出しなどで、関数の階層を深くしたくないなどという本当に特殊な場合だけです。)
do whileで記述できる文はwhileで必ず記述できます。do whileを使ったからといって記述が簡潔になるかといえばそうでもないです。逆に普段使用していない分だけ意図がわかりにくくなると感じます。
do whileで記述できる文はwhileで必ず記述できるのでwhileを使うロジックを考えることをお勧めします。
Perlにはredo文がありますが、redoを使わなくても同じロジックを記述することができます。redoは何回か使用したことがあるのですが、redoを使ったプログラミングはとてもわかりにくくなると感じます。redoを使用しなくても同じロジックは必ず記述できるのでredoを使わないロジックを考えることをお勧めします。
サブルーチンを定義するときにプロトタイプという型を指定できる機能がありますがこれは使用しません。
# プロトタイプは使用しないことsubfunc($@){ ...}
Perlでは明示的に型を指定しなくてもどのような型の引数も受け取れますし、引数の個数もいくつでもかまいません。ですのでプロトタイプで型を指定したり個数を指定したりする必要はまったくないのです。ですので必ずプロトタイプを指定しないサブルーチンの定義を行いましょう。
subfunc{ ... }
Perlには例外処理がないと思っている人もいるかもしれません。Javaのような例外オブジェクトというものはありませんが、簡潔な例外機構を備えています。
まずは旧来のエラー処理であった戻り値にundefを返却する方法を見ます。エラーが発生したときに単独のreturnを記述するとスカラコンテキストの場合はundefがリストコンテキストの場合は空のリスト () が返却されます。
# エラーが発生したときにundefを返却するsubfunc{my$arg =shift; ...# エラー処理if ($error) {return; }# エラーが起こらなかった場合の正しい値return$val;}
そして関数を呼び出す側でエラー処理を記述します。
my$val = func();# $valが偽値だったらプログラムを終了die"Error"unless$val;
この記述の問題点はfuncを使う人が戻り値のチェックを怠るとプログラムは先に進んでしまうということです。
ですので現在的なPerlではエラーを伝えるときにdieを使って例外を投げます。
# エラーが発生したときにdieを使って例外を投げるsubfunc{my$arg =shift;# なんらかの処理# dieを使って例外を投げるif ($error) {die"Error message"; }# エラーが起こらなかった場合の正しい値return$val;}
このようにするとfuncを呼び出してエラーが発生したときはエラーメッセージを表示してプログラムは終了します。
# エラーが発生した場合はエラーメッセージを表示してプログラムが終了func();
プログラムを終了させたくない場合はevalブロックで受けます。これはJavaでいうcatchだと思ってください。エラーが発生した場合は$@という特殊変数にエラーの内容が設定されますので、この変数をチェックすることでエラーが発生したかどうかを調べることができます。
eval { func() };if ($@) {# エラーが発生した場合の処理を記述}
引用をストックしました
引用するにはまずログインしてください
引用をストックできませんでした。再度お試しください
限定公開記事のため引用できません。