理系学生日記

おまえはいつまで学生気分なのか

Data::Dumper に代わる Data::Printer

LL でデバッグと言えばデバッガではなく print デバッグ、という人は多いと思います。ぼくはもっぱら print デバッグです。
いまこのタイミングでこのオブジェクトはどんな値を持っているんだろう、というときは、Perl だったらもっぱら Data::Dumper を使って、

sub p { print Dumper @_ }

p $object;

なんてのを良く書いてたんですけど、$object がクソみたいにデカいモジュールのオブジェクトだったりすると、ターミナルが溢れて(ぼくが)死んだりしてました。DateTime とか HTTP::Request あたりとか大変ですね。

で、ちょっと Data::Printer 良いよって声を聞いたので試してみたのでした。Class::MOP 依存だがな!!!

基本的な使い方

Data::Printer を use すると、p っていう関数がデフォルトでインポートされます。
この p にオブジェクトを食わせると、デフォルトでは public/private メソッド、internal とかがダンプされます。

use DateTime;
use Data::Printer;

p(DateTime->now);


色ついてるし、キレイにフォーマットされてますけど、ちょっとまだ内容が多いですね。

なので、p に DateTime が渡されたときに、何をダンプするかを決めておきましょう。ここでは、YYYY-MM-DD にしておきましょうか。

use DateTime;
use Data::Printer {
    filters => {
	'DateTime' => sub { shift->ymd },
    },
};

p(DateTime->now); # 2012-01-23 が出力される

use するときに引数を与えています。ここでは DateTime が渡されたら、その ymd メソッドを出力するっていう感じですね。わかりやすくなって大変よろしい。

関数食わせる

deparse オプション有効にすると、B::Deparse を使ってサブルーチンを展開できます。

use DDP {
    deparse => 1,
};

my $sub = sub { print join "\n" => @_; };
p($sub);
# \ sub {
#         use warnings;
#         use strict 'refs';
#         print join("\n", @_);
#     }

Data::Printer って打つのメンドくさいんだけど…

日頃よく使うモジュールになりそうなのに、use Data::Printer って打つのメンドくさい場合には、Data::Printer のエイリアスとして DDP って用意されてますので、

use DDP;

って書くと良いです。

メソッドチェインでも有利

p メソッドはデフォルトでは STDERR に出力を行いますが、これを標準出力にしたいときは

print p($obj);

なんてすれば良いです。これは、標準では、p 関数は

  • void context では何も返却しない
  • void context でなければ、出力しようとする文字列を返却する(出力はしない)

っていう振る舞いをするからです。

もし、どんなときでも返却(return) はするようにしたいときは、return_value オプションを真にしてやれば良いです。

use DDP {
    filters => {
	'DateTime' => sub { shift->ymd },
    },
    return_value => 'pass'
};

これがどこで役に立つかというと、メソッドチェインの間に入れられるんですね。

use DateTime;
use DDP {
    filters => {
	'DateTime' => sub { shift->ymd },
    },
    return_value => 'pass'
};

DateTime->now->DDP::p->add(days => 1)->DDP::p;
# 2012-01-23
# 2012-01-24

おお、これは便利だ。

一々 DDP に引数を食わせるのメンドくさいんだけど

上記の例で、一々 DateTime を使うところ全部に

    filters => {
	'DateTime' => sub { shift->ymd },
    },

書くのマジめんどくさいですね。
そういうときは、$HOME に .dataprinter ってファイルを用意して以下のように書いておきましょう。

{
    filters => {
        'DateTime' => sub { shift->ymd; }
    }
}

すると、Data::Printer を使うときはいつもこのファイルが読み取られるようになるので、use DDP; するだけで、DateTime が YYYY-MM-DD 化されます。

use DateTime;
use DDP;

p(DateTime->now);
# 2012-01-23

というわけで、Data::Dumper の持つ "シリアライズ" 機能が必要ない場合は、わりと Data::Printer 良いのでは。