Practice of Programming

プログラム とか Linuxとかの話題

Util::All だいたい終わった

Util::Allですが、終わったというか、キリがないので、そろそろやめる。
CPANにあげようかと思ってるんですが。Allなんて厚かましいのはいいんだろうか。


現在、250以上の関数(まじめに数えてない)を集めました。一部は自分で新たに書いたものや、wrappingしたものもあります。70個くらいのモジュールに依存してます。全部の関数の説明の podは3500行近い(Util::All::Manual)。
なお、ほとんどの説明は自分で書いたものではなく、Pod::Sectionで、抜き出したものを埋め込んでいます。ので、ちょっと見づらいところがあるかも。あと、説明書いてなくて、代わりに関数定義がそのまま埋められてるのもあったりしますが、それはそのうち直します。
一部をのぞいて、script/definition/functions.yml を変更して、script/create_util_all.pl で生成しています。一部perltidyで整形したりしているので、見た目が変なコードも混じっています("Object"->newみたいな)。


ちょっと紹介。

automatically strict/warnings/perl5.10 features

Modern::Perl的なものです。

use Util::All;

で、 strict/warnings/mroがc3/featur 5.10 がかかります。バージョンが古い場合は、MRO::Compatなんかを使うようにしてます。5.10以上じゃなければ、feature 5.10は使われません。
余計なお世話!って人は、以下で無効にできます。

use Util::All -modern => []; # disable -modern

-argv

自動で使われない pragma 的なものとして、-argv というのがあります。

use Util::All -argv;

で、@ARGVに、utf8 flag がつきます。引数を与えることで、decode じゃなくて、encode とかもします。

-dumper

よく使うので、デフォルトでimportさせています。Data::Dumpの関数と追加で作ったものが含まれます。

use Util::All -dumper;
dump($var);
deep_dump([$var, sub { hoge => 1}]); # 関数の中身も文字列としてdumpする
p($var); # 名前をrubyからもらっただけで、dump と同じ

どんな実装してるか気軽に調べられるかも。

% perl -MUtil::All -e 'deep_dump(\&deep_dump)'
sub (@) {
            package Util::All;
            use warnings;
            use strict 'refs';
            local $Data::Dumper::Deparse = 1;
            local $Data::Dumper::Terse = 1;
            if (not defined wantarray) {
                print STDERR Data::Dumper::Dumper(@_);
            }
            else {
                return Data::Dumper::Dumper(@_);
            }
        }

-datetime

DateTimeオブジェクト関連
end_of_month は limitをデフォルトで指定しています。import時の引数で変更することもできます。

use Util::All -datetime;
my $dt = now(); # DateTime object
my $new_dt = $dt + years(2) + month + days(30);
my $dt = datetime_parse("2010/01/01");

パースですが、perlのバージョンによりDate::Manipを使うか使わないか決めてます。

-oo

オブジェクト指向を手軽に。Class::Accessor::FastとClass::Data::Inheritableのwrapperです。

package Hoge;

use Util::All -oo;
accessors("foo", "bar");
classdata("Foo");

package main;

my $o = Hoge->new;
$o->foo(100);
$o->bar(50);
Hoge->Foo(500);
say $o->foo + $o->bar + Hoge->Foo; # 650

-unicode

Unicode::CharNameから。

use Util::All -unicode;
say unicode_name("\x{2026}"); HORIZONTAL ELLIPSIS
say unicode_block("\x{2026}"); General Punctuation

-charset

Unicode::Japaneseとか。Encodeのwrapperとか。

use Util::All -charset;
char_convert($var, $to_code, $icode); # Jcode的な
jfold($var, 100); # 文字幅を一応考慮した感じ
h2z($var);
z2h($var);
h2z_alpha($var);

-number

数字にまつわるいろいろ。Number::Formatですね。

use Util::All -number;
say number_commify(1000); # 1,000
say to_number('1KiB'); # 1024

-uri

URIモジュールは1.35あたりで、utf8まわりの変更が入っているようなので、その辺バージョンみて、考慮するようにしました。
(CentOS5 かなんかのURIモジュールが1.35で、それをあげると、他のに影響がでて微妙だったっていう...。local::lib使えって話ではありますが)

use Util::All -uri;
say make_uri("http://example.com", {foo => "bar", buz => [1,2,3]}); # http://example.com?buz=1&buz=2&buz=3&foo=bar
say make_query({foo => "bar", buz => [1,2,3]}); # buz=3&buz=2&buz=1&foo=bar

-email

Email::Sender の wrapper です。

use Util::All -email;
send_email([From => $from, To => $to, Subject => $subject],
           {charset => $charset},
           {content_type => "text/plain", charset => "utf8"}, $body);

send_email([From => $from, To => $to, Subject => $subject], #ヘッダ
             {charset => $charset}, # アトリビュート
             [ # マルチパートなら array ref の array ref
              [ {content_type => "text/plain", charset => "utf8"}, $body ],
              [ {content_type => "text/plain", charset => "utf8"}, $body2],
              "/path/to/file",
           ]);

-string

say camelize("hoge_fuga"); # HogeFuga
say decamelize("HogeFuga"); # hoge_fuga
$fh = to_fh($var);
$fh = to_fh(url => "http://example.com/");

to_fh はIO::Stringのwrapperです。便利だと思うんだが、分類がおかしいか。

-csv

Text::CSV使ってます。nextメソッドは非常に微妙です(resetとかはありません)。binary は、デフォルトで1です。
最初、Text::CSV_XSを明示的に使ってたんですが、Text::CSVに次のように書いてたので、やめました。
"Text::CSV provides facilities for the composition and decomposition of comma-separated values using Text::CSV_XS or its pure Perl version."

  use Util::All -csv;
  
  my $csv = parse_csv($file_or_fh);
  while (my $ar = $csv->next) {
     print "@$ar\n";
  }
  
  my $csv = parse_csv($file_or_fh, ['name', 'age']);
  while (my $hr = $csv->next) {
     print join " ", %$hr, "\n";
  }
  
  # pass options to Text::CSV
  use Util::All -csv => [-args => {binary => 0, eol => "\r\n"}];

ロード速度

いっぱい読めば読むほど遅いです。

Core2Duo 2.13GHz

perl -e 'use Util::All -all'  0.74s user 0.04s system 99% cpu 0.783 total
perl -e 'use Util::All -list'  0.07s user 0.01s system 103% cpu 0.077 total

all は10倍くらい遅いようです。


同じ種類でも、関数を指定するのとしないのでは、速度が変わってきます。

perl -e 'use Util::All -datetime'  0.21s user 0.03s system 97% cpu 0.245 total
perl -e 'use Util::All -datetime => ["years"]'  0.14s user 0.00s system 89% cpu 0.157 total

普通のimportとの比較。当然のようにオーバーヘッドがひどいですね! 初回だけなので、このベンチマークはあまり意味はないと思いますが。

                   Rate        util-all list-more-utils
util-all         1552/s              --            -98%
list-more-utils 84454/s           5341%              --

依存モジュールたち...

てわけで、お世話になっている依存モジュールは以下のとおりです。

Benchmark, CGI::Util, Carp, Class::Accessor::Fast, Class::Data::Inheritable, Clone, Data::Dump, Data::Dumper, Data::Recursive::Encode, Data::Serializer, Data::Structure::Util, Data::Util, Date::Manip, Date::Parse, DateTime,DateTime::Duration, Devel::Cycle, Devel::Size, Digest::MD5, Digest::SHA, Email::MIME, Email::Sender::Simple, Encode, Encode::Argv, File::Copy, File::Find, File::Path, File::Slurp, File::Temp, HTML::Entities,HTTP::Request::Common, Hash::Util, IO::Prompt, IO::String, Image::Info, Imager, JSON::XS, LWP::UserAgent, List::MoreUtils, List::Pairwise, List::Util, MIME::Base64, MIME::Base64::URLSafe, MIME::Types, Math::BaseCalc,Number::Format, Path::Class, Scalar::Util, Storable, String::CamelCase, String::Util, Template, Term::Encoding, Text::CSV, Tie::IxHash, Time::HiRes, Toolbox::Simple, Try::Tiny, URI, URI::Escape, URI::Split,Unicode::CharName, Unicode::Japanese, Unicode::String, XML::Parser, XML::Simple, YAML::XS

自分では使ってるのか?

使ってますが、全関数を使ってるわけではないです。多いし...。
とりあえず、ワンライナーとかで便利かも(ぇ