型グロブ
型グロブってなんぞ、と言うわけで簡単にまとめてみる。
#!/usr/bin/perl use strict; use warnings; use Data::Dumper; our $args = 1; our @args = (1, 2, 3); our %args = (1 => 999); sub args { print "Hello, world!\n"; } print Dumper ${ *main::args }; print Dumper \@{ *main::args }; print Dumper \%{ *main::args }; &{ *main::args }; exit;
別に上記のように書く必要性はないんだが、型ブログってのはシンボルテーブルにあるファニー文字を省いた同名の変数を一括に取得できる機能とでもいうのだろう。考え方としてシンボルテーブルには型グロブごとに領域が分割されていて、その型グロブの中にスカラー、配列、ハッシュ等々さらに分割されているんだろうね。と言うわけで下記にシンボルテーブルの内容を表示してみる。
print Dumper \%main::;
「exit」の上部に追加、表示結果、
$VAR1 = 1; $VAR1 = [ 1, 2, 3 ]; $VAR1 = { '1' => 999 }; Hello, world! $VAR1 = { 'version::' => *{'::version::'}, '/' => *{'::/'}, 'stderr' => *::stderr, 'SIG' => *::SIG, 'Tie::' => *{'::Tie::'}, 'utf8::' => *{'::utf8::'}, '"' => *{'::"'}, 're::' => *{'::re::'}, 'DynaLoader::' => *{'::DynaLoader::'}, 'mro::' => *{'::mro::'}, 'strict::' => *{'::strict::'}, 'stdout' => *::stdout, '^R' => *{'::^R'}, 'Regexp::' => *{'::Regexp::'}, '_<C:/strawberry/perl/lib/auto/Data/Dumper/Dumper.dll' => *{'::_<C:/st rawberry/perl/lib/auto/Data/Dumper/Dumper.dll'}, 'UNIVERSAL::' => *{'::UNIVERSAL::'}, 'overload::' => *{'::overload::'}, '$' => *{'::$'}, '^RE_TRIE_MAXBUF' => *{'::^RE_TRIE_MAXBUF'}, 'Data::' => *{'::Data::'}, '_<..\\universal.c' => *{'::_<..\\universal.c'}, 'BEGIN' => *::BEGIN, '_<..\\mro.c' => *{'::_<..\\mro.c'}, '!' => *{'::!'}, 'IO::' => *{'::IO::'}, '^X' => *{'::^X'}, '_' => *::_, 'Exporter::' => *{'::Exporter::'}, 'Internals::' => *{'::Internals::'}, 'STDIN' => *::STDIN, 'Config::' => *{'::Config::'}, 'warnings::' => *{'::warnings::'}, 'DB::' => *{'::DB::'}, '_<perllib.c' => *{'::_<perllib.c'}, '1' => *{'::1'}, '^WARNING_BITS' => *{'::^WARNING_BITS'}, 'CORE::' => *{'::CORE::'}, 'Win32CORE::' => *{'::Win32CORE::'}, 'attributes::' => *{'::attributes::'}, 'stdin' => *::stdin, 'ARGV' => *::ARGV, 'INC' => *::INC, 'ENV' => *::ENV, 'Scalar::' => *{'::Scalar::'}, '_<..\\perlio.c' => *{'::_<..\\perlio.c'}, '_<Win32CORE.c' => *{'::_<Win32CORE.c'}, 'XSLoader::' => *{'::XSLoader::'}, 'B::' => *{'::B::'}, 'args' => *::args, 'main::' => *{'::main::'}, 'Carp::' => *{'::Carp::'}, 'Win32::' => *{'::Win32::'}, 'PerlIO::' => *{'::PerlIO::'}, '0' => *{'::0'}, ' => *{':'}, '_<..\\xsutils.c' => *{'::_<..\\xsutils.c'}, '@' => *{'::@'}, 'STDOUT' => *::STDOUT, ']' => *{'::]'}, 'Dumper' => *::Dumper, '_<win32.c' => *{'::_<win32.c'}, 'STDERR' => *::STDERR, 'bytes::' => *{'::bytes::'}, '_<Dumper.c' => *{'::_<Dumper.c'}, '_<dl_win32.c' => *{'::_<dl_win32.c'} };
見にくいがどこかに「args」があるはず。「$args」「@args」「%args」「args」全部、「*main::args」で管理されていますね。面白いですね。型ブログがどんなものか大体わかったが使いどころがわからない。いつ、どこで利用するのだろうが?
package Foo; use strict; use warnings; sub hello { print "Hello, world!\n"; } package main; use strict; use warnings; *h = \&{ *Foo::hello }; h(); exit;
なんか微妙。パッケージ「Foo」の「hello」メソッドが実行されるのだがこんな事やらなくてもと思ってしまう。また、型グロブ同士の受け渡しは下記のようになる。
package Foo; use strict; use warnings; our $aaa = 1; our @aaa = (1,2,3,4); our %aaa = (key => 9999); sub aaa { return; }; package main; use strict; use warnings; use Data::Dumper; *f = *Foo::aaa; print Dumper ${ *f }; print Dumper \@{ *f }; print Dumper \%{ *f }; print &f(); exit;
「*f」は「*main::f」とも書ける。もっともっと役に立つと言うよりも良く使われている利用法が知りたい。と言うわけで、下記のようなものを作ってみた。
# Bar.pm package Bar; use strict; use warnings; sub import { my $class = shift; my $pkg = caller; $class = (ref $class or $class or __PACKAGE__); no strict 'refs'; *{ $pkg . '::hello' } = \&hello; *{ $pkg . '::morning' } = \&morning; *{ $pkg . '::night' } = \&night; } sub hello { print "Hello, world!\n"; } sub morning { print "Good Morning\n"; } sub night { print "Good Night\n"; } 1;
実行スクリプト、
#!/usr/bin/perl use strict; use warnings; use lib qw(.); use Bar; &hello(); &morning; night; exit;
なるほど、少し分かった気がする。もう少し進めてみる。
package Bar; use strict; use warnings; sub import { my $class = shift; my @methods = @_; my $pkg = caller; return if (!scalar(@methods)); $class = (ref $class or $class or __PACKAGE__); { no strict 'refs'; foreach my $method (@methods) { *{ $pkg . '::' . $method } = \&{ $method }; } } } sub hello { print "Hello, world!\n"; } sub morning { print "Good Morning\n"; } sub night { print "Good Night\n"; } 1;
実行スクリプト、
#!/usr/bin/perl use strict; use warnings; use lib qw(.); use Bar qw(hello morning); &hello(); &morning(); exit;
実行結果、
Hello, world! Good Morning
おおお、面白いね。利用したいモジュールがちゃんと利用できた。ちょっと仕組みがわかった気がする。ただ現在ってもっと違う仕組みが利用されている気がする、EXPORT_OKとか、とは言え、こういった仕組みを理解しないでいいかといえば、それは違うだろと思うわけで、型グロブって面白いね。では
useの話かも
「use」はモジュールをインポートするものですが、
package Foo; use strict; use warnings; sub new {}; sub foo {}; 1;
上記モジュールを利用するスクリプト
#!/usr/bin/perl use strict; use warnings; use Data::Dumper; use lib qw(.) use Foo; print Dumper \%{Foo:}; exit;
出力結果
$VAR1 = { 'BEGIN' => *Foo::BEGIN, 'foo' => *Foo::foo, 'new' => *Foo::new, 'import' => *Foo::import };
これは、Fooモジュールのシンボルテーブルの中身であってFooモジュールで定義されている変数や関数になるわけだが、「BEGIN」は良いとして、「import」ってなんぞや?そんなものは書いた覚えがない、勝手に作られたのかな〜というわけなんだが、Perlの仕様で「use」した場合、自動的に「import」メソッドを呼び出すとの事。
ただし〜モジュールを呼び出す側で、ある事をするとですね。面白い事になる。
#!/usr/bin/perl use strict; use warnings; use Data::Dumper; use lib qw(.) use Foo (); print Dumper \%{Foo:}; exit;
出力結果
$VAR1 = { 'BEGIN' => *Foo::BEGIN, 'foo' => *Foo::foo, 'new' => *Foo::new, };
「import」が消えた!「use」する際にモジュールに「()」を書いて明示的にすると「import」メソッドが呼ばれません。面白いね。よく入門書を見たりすると、モジュールごとに「()」があったりなかったりしてあるが、特に深く考えなかった。明示的に「()」を書いて「import」メソッドを呼ばないようにするって事はメモリの節約になるから、そういった細かなところまで気を配っているんですね。面白いよ。ではでは
requireとuse
なんか、Perlにはモジュールをロードするのに「require」と「use」があるが、最近は「use」が主流だけどPerl4の時は「require」でやってたよね。で何が違うのかと言うわけだが・・・
名称 | 説明 |
---|---|
require | 実行時に読み込まれる |
use | コンパイル時に読み込まれる |
こんな感じ〜わかんないよ!「use」はコンパイル時に読み込まれるってのはそのままだね。Perlってインタプリタ言語だからコンパイルされないんじゃ〜と思うのだが、いやいや〜CPUは機械語しか理解できないのでPerlもコンパイルされますが、そのタイミングが重要なんだと思う。実行ファイルを作るのがコンパイラ、ブロックごとに実行しながらコンパイルするのがインタプリタって理解しているのだが、それで良いよね。で「use」ってのはブロックごとにでもコンパイルされているので、その時点で読み込まれる。対して「require」は実行されるときに読み込まれる。コンパイラされたコードを実行する際に読み込まれるんだと思う。「require」の使いどころは、処理の分岐でモジュールをロードする際に「require」でやればいいと思う。「use」だと、問答無用で全て読み込んじゃうからメモリがもったいないのよね。
if ($condition) { require ModuleA; ... } else { require ModuleB; ... }
まあ〜そんだけ〜では
内容 | |
---|---|
BEGIN | コンパイル時に実行される |
END | 終了時に実行される。dieなどで終了した場合も呼び出される |
package Foo; BEGIN { ... } sub new { ... } sub END { ... } 1;
あと、INITやCHECKなどのサブルーチンもあるんだが、ようわからん。また調べとく。
シンボルテーブルのお話
Perlにはパッケージってのがあってパッケージごとに管理できるわけだよ。モジュールとか変数とかを、でだ、そのパッケージごとにシンボルテーブルがあって、それを見てみたい。
#!/usr/bin/perl use strict; use warnings; foreach (values %main::) { print $_, "\n"; } exit;
出力結果
*main::version:: *main::/ *main::stderr *main::_<perllib.c *main::Tie:: *main::utf8:: *main::" *main::^WARNING_BITS *main::re:: *main::CORE:: *main::DynaLoader:: *main::mro:: *main::strict:: *main::stdout *main::Win32CORE:: *main::attributes:: *main::^R *main::stdin *main::ARGV *main::INC *main::ENV *main::Regexp:: *main::_<Win32CORE.c *main::_<..\perlio.c *main::UNIVERSAL:: *main::$ *main::^RE_TRIE_MAXBUF *main::main:: *main::Carp:: *main::Win32:: *main::PerlIO:: *main::_<..\universal.c *main::0 *main::BEGIN *main:: *main::@ *main::_<..\xsutils.c *main::_<..\mro.c *main::STDOUT *main::IO:: *main::^X *main::_ *main::STDERR *main::_<win32.c *main::Internals:: *main::STDIN *main::warnings:: *main::DB::
mainパッケージには色々なものがシンボルテーブルにありますね。ちょっとスクリプトを変更。
#!/usr/bin/perl use strict; use warnings; our $aaa = 1; our @bbb = (1, 2, 3, 4, 5); our %ccc = (1 => 111, 2 => 222, 3 => 333); sub ddd { return; } foreach (values %main::) { print $_, "\n"; } exit;
*main::version:: *main::/ *main::stderr *main::_<perllib.c *main::Tie:: *main::utf8:: *main::" *main::^WARNING_BITS *main::re:: *main::CORE:: *main::DynaLoader:: *main::mro:: *main::strict:: *main::stdout *main::Win32CORE:: *main::attributes:: *main::^R *main::stdin *main::ARGV *main::INC *main::ENV *main::Regexp:: *main::_<Win32CORE.c *main::_<..\perlio.c *main::UNIVERSAL:: *main::ccc *main::$ *main::bbb *main::^RE_TRIE_MAXBUF *main::main:: *main::Carp:: *main::Win32:: *main::PerlIO:: *main::_<..\universal.c *main::0 *main::aaa *main::BEGIN *main:: *main::@ *main::_<..\xsutils.c *main::_<..\mro.c *main::STDOUT *main::IO:: *main::^X *main::_ *main::STDERR *main::_<win32.c *main::Internals:: *main::STDIN *main::warnings:: *main::DB:: *main::ddd
沢山あってわかりにくいですが、先ほど追加したスカラー、配列、ハッシュ、関数は追加されていますね。凄い!注目は各種変数の先頭が「our」になっていることです。なぜかというと、「my」だと別領域に格納されてしまいます。
シンボルテーブルの領域の事を「スタッシュ」とも言い、「my」で宣言された変数を格納する場所を「スクラッチパッド」と言うそうです。面白いですね。そんだけ!
どうでもいい話ですけど、
my $scalar = 1; my @array = (1, 2, 3, 4); my %hash = (1 => 1111);
と
my $scalar = 1; my $array = (1, 2, 3, 4); my $hash = {1 => 1111};
が私の頭の中でごちゃごちゃになっていたんだが、すっきりした事。上の方はファニー文字で、そのものを表しているから問題ないが、下は全部スカラーとして表されている。スカラーに対してハッシュを格納する場合、ハッシュのリファレンスを格納しているんだよね。「{}」がハッシュのリファレンスを表すなんて理解できてなかったから、もやもやしていたが解決?だよね。
もちろんスカラー変数を通してハッシュにアクセスするには、「$hash->{1}」としなければならんのだが、なんかすっきりした。ではでは
Tie::Fileは少し面白いかも
tieについて調べていたら、Tie::Fileってのが面白そうだったんで使ってみた。
#!/usr/bin/perl use strict; use warnings; use Tie::File; tie my @array, 'Tie::File', './file_name.txt' or die $!; foreach (@array) { tr/a-z/A-Z/; } print sort @array; untie @array; exit;
ん〜あまり面白さの伝わらないコードですね。まあ、こんな感じ。ファイルをオープンしないで、その行を配列に対応させたって事なんだが、別に「open」でも同じだよね。
open my $fh, '<', './file_name.txt' or die $!; foreach (<$fh>) { tr/a-z/A-Z/; } close $fh
でも、これではファイルに書き込んでないからファイル自身は変更ないね。でも、Tie::Fileで結びつけると、きれいにファイルの中身も変更されちゃいますね。ファイルに対して変更を加える場合にはTie::Fileを利用した方が便利かもしれませんね。わざわざ書き込む手間が省けますから。
まあ〜どっちを使うかはその人しだいだけど、tieってあんまり一般的じゃない気がするから業務では控えた方が良いのかも知れませんね。保守できなくなったら嫌じゃないですか。ではでは
tieってなんぞ
久しぶりにPerlにさわって見た。でだ、残念な事に全て忘れていたよ。と言うわけで忘れないようにメモしておく。
tieとは、
「オブジェクトではない、perlの組み込みデータタイプを裏でオブジェクト化する仕組み」 by 弾<参照>
の事だ。上記一文でもやもやしていたのが一気に晴れたよ。なるほど!と思った。今までtieを見たことはあったが実際の業務で必要だったかと言うと、そういった事はなくtieを知らなくても支障はなかった。tieってPerl4でオブジェクト指向っぽくコーディングしたい場合に役立つ機能だよね。Perl5ではなんちゃってオブジェクト指向の実装ができるから必要ないと思うんだが、知っておくことは必要だと思う。
use strict; use warnings; { package Foo; sub TIESCALAR { my $class = shift; my $self = {}; bless $self, $class; } sub FETCH { print "fetch\n"; } sub STORE { print "store\n"; } sub DESTROY { print "destroy\n"; } } tie my $foo, 'Foo'; $foo = 1; my $bar = $foo; exit;
で実行結果は、
tiescalar store fetch destroy
面白くもなんともないね。
TIESCALARってのがtieで変数とパッケージを結びつけた際、一番最初に実行されるところ。コンストラクタと解釈してもいいと思う。次にFETCHだけど、FETCHはtieで結びつけた変数に対してアクセスしたときに呼ばれる。でSTOREは、その変数を他の変数等にストアした際に呼ばれ、DESTOROYは必要なくなったときに呼ばれる。
あと、TIEはSCALARだけじゃなく、ARRAY、HASH等と結びつける事ができ、それらはSCALARと比べたら多少複雑になる。実装しなければならない専用のメソッド(FETCHやSTORE等)が増える。とは言え基本的な考え方は変わらない。
で、tieをいろいろ調べてたんだが、tieって微妙だよね。例えば実装できるメソッドが既に決まっているって事でしょ。FETCHやSTORE等々、それら以外を実装したいと思えばどうしたらよいの。$varという変数に対して、新規にメソッドを追加したいのよ。
use strict; use warnings; { package Bar; sub TIESCALAR { ... } sub FETCH { ... } sub STORE { ... } sub DESTROY { ... } sub COUNT { my $self = shift; my $scalar = shift; return length($scalar); } } tie my $bar, 'Bar'; $bar = 9000; print $bar->COUNT; # 4 と出力される
とかだったら面白いよね。新しいメソッドを追加できたら、その動作をいろいろと変更できたら使いやすいと思うんだが、残念ながらできそうにないね。まあオブジェクト指向って考え方が後付された言語だから仕方ないかもって素直にblessやMoose使ってやれってことですね。わかりました。
でも、知っておくことは必要だと思った今日この頃です。では。
コンテンツの圧縮
XMLデータ等々を取得する場合、そのデータを圧縮し取得できるようだ。話には聞いていたが詳しく知らなかった。
>>> import urllib2, httplib >>> httplib.HTTPConnection.debuglevel = 1 >>> request = urllib2.Request('取得するXMLデータのURL') >>> request.add_header('Accept-encoding', 'gzip') >>> opener = urllib2.build_opener() >>> f = opener.open(request) >>> compresseddata = f.read() >>> len(compresseddata) 8427 >>> import StringIO >>> compressedstream = StringIO.StringIO(compresseddata) >>> import gzip >>> gzipper = gzip.GzipFile(fileobj=compressedstream) >>> data = gzipper.read() >>> print data ・・・ >>> len(data) 24102
面白いね。これって「gzip」がサーバ側でサポートされていないと使えないよね。まあLinux系ならサポートされているだろうが、Windows系は注意が必要だよね。コンテンツ側で利用するXMLデータやその他データにも利用できそうだよね。勉強になった。
他の言語でもできるよね。これってリクエストの「Accept-encoding」に「gzip」を設定すれば、要求された側はコンテンツを指定された方式で圧縮してレスポンスを返すって事ですよね。
またクライアント側では「Conntent-Encoding」のヘッダーを見て送信されてきたデータが圧縮されているのか、されていないのかが判断できるので処理を書き分ける事も可能。
ん〜HTTPヘッダを意識したコーディングをやって覚えがないのは痛いな。これからは注意しよ。
参考その1:http://www.kawa.net/works/perl/contenc/gzip-test.html
参考その2:http://www.dengeki.ne.jp/soft/cgi/ziptrans.html