Perlのエラーメッセージのコーナー。
Perlのエラーメッセージが表示される最小のプログラムを書いて、どういうときにどう怒られるかを研究する。
今回は
%s did not return a true value

を研究する。
(※今日の記事はスッキリ終わりません!)
これは、

(主語S):%s
(動詞V):did not return
(目的語O): a true value

という第2文型の文で、

(主語S):%s
(動詞V):は返さなかった
(補語C):真値を

という意味になる。
%sは真値を返さなかった
という訳になる。

これは、useで取り込むモジュール ファイルが真の値を返さなかった時に発生する。

以下は、昨年6月に寄稿したSoftware Design誌の原稿「Perlによるオブジェクト指向入門」の例題プログラムで解説する。

以下は、誌面に載った(エラーにならない)プログラムである。
まずmyDate.pmというモジュールを作成する。
# myDate.pm -- 日付のモジュール

use strict;
use warnings;

package myDate;

use overload
 '""' => 'toString',
 '+' => 'add',
;

sub new {
 my ($class, $date) = @_;
 unless (defined $date and $date =~ /(\d+)(\d\d)(\d\d)/) { # 5桁以上の数字かチェック
  die "myDate: date $date should be 5 or more digits numeric!";
 } else {
  bless { date => $date, y => $1, m => $2, d => $3 } => $class;
 }
}

sub add {
 my ($self, $add) = @_;
 my ($date, $y, $m, $d) = ($self->{date}, $self->{y}, $self->{m}, $self->{d});

 for (1..$add) { # 加算する日付のぶんループする
  if ($m == 12 and $d == 31) { # 大晦日
   ++$y; $m = 1; $d = 1;
  } elsif ((&leap($y) and $m == 2 and $d == 29) # 月末
  or ($m == 2 and $d == 28)
  or (($m == 4 or $m == 6 or $m == 9 or $m == 11) and $d == 30)
  or ($d == 31)) {
   ++$m; $d = 1;
  } else { # それ以外
   ++$d;
  }
 }
 $date = $y. sprintf("%02d", $m). sprintf("%02d", $d); # 1桁の場合はゼロを付加
 bless { date => $date, y => $y, m => $m, d => $d } => ref $self;
}

sub toString { # 文字列スカラーに変換する
 return shift->{date}; # shiftは第1引数=オブジェクトそのもの
}

sub leap { # うるう年なら真を、それ以外なら偽を返す
 my $y = shift;
 return 1 if $y % 400 == 0;
 return 0 if $y % 100 == 0;
 return 1 if $y % 4 == 0;
 return 0;
}

1;
この最後の「1;」がミソで、モジュールが真の値を返すためにムリヤリ入れている。

このモジュールを使うメインプログラムを書く。
#! /bin/perl
#
# myDateTest.pl -- 日付のテスト

use strict;
use warnings;
use myDate;

my $date = myDate->new(shift);
print "Date is $date\n";

$date = $date->add(10);
print "10 days after is $date\n";
print "30 days after is ", $date + 30, "\n";
では実行してみる。

$ myDateTest.pl 20140305
Date is 20140305
10 days after is 20140315
30 days after is 20140414
$
エラーは出ていない。

では、末尾の「1;」を「0;」に変えてみる。
# myDate.pm -- 日付のモジュール
use strict;
use warnings;

package myDate;

。。中略。。

0;
である。
$ myDateTest.pl 20140305
myDate.pm did not return a true value at /Users/query1000/perl/myDateTest.pl line 7.
BEGIN failed--compilation aborted at /Users/query1000/perl/myDateTest.pl line 7.
$
たしかに目的のエラーメッセージが表示された。

さて、どのようなものが真として解釈され、どのようなものが偽として解釈されるのだろうか。
こうなっている。

(偽になるもの)
・数値 0
・文字列 ""
・文字列 "0"
・空リスト
・undef
・偽を返した関数値

(真になるもの)
・ゼロ以外の数値
・空文字列、"0"以外の文字列
・中身のあるリスト
・真を返した関数値

これは「真実と偽り」とは関係がない。
上のモジュールを以下のようにしてみる。
# myDate.pm -- 日付のモジュール
use strict;
use warnings;

package myDate;

。。中略。。

”郵便ポストは青い”;
実行する。
$ myDateTest.pl 20140305
Date is 20140305
10 days after is 20140315
30 days after is 20140414
$
明らかな嘘、いつわりでモジュールを締めくくったにも関わらず、モジュールはちゃんとuseされ、プログラムはちゃんと実行された。
このように、いくらでも凝った値をモジュールを締めくくる真値として使うことができるが、こんなことで個性を発揮してもしょうがないので「1;」と書いたほうが良い。
ラクダ本のuse関数の解説には「useはrequireを呼ぶ」と書かれていて、requireの解説には「初期化コードが正しく終了したことを示すためにモジュールは真を返す。ために慣例的に1;と書く」と書かれている。

なお、Perl 5.14からはパッケージをブロックにすると、package関数が真を返すために1;を書く必要がなくなるそうだ。

Perl 5.13.2 がもたらした package NAMESPACE BLOCK 構文があたえるもの - blog.64p.org

モジュールの末尾に「1;」と書きたくない人のためのパッケージブロック構文 - サンプルコードによるPerl入門 ~ Perlで楽しくプログラミングを学ぼう ~

こうなる。
# myDate.pm -- 日付のモジュール

use strict;
use warnings;

package myDate {

。。中略。。

}
やってみた。
$ myDateTest.pl 20140305
myDate.pm did not return a true value at /Users/query1000/perl/myDateTest.pl line 7.
BEGIN failed--compilation aborted at /Users/query1000/perl/myDateTest.pl line 7.
$
ダメだー。
「}」の前に「1;」を入れると無事に実行できる。
ううん、研究課題である。

ChancelWindowTruth