
いい点に気づかれました。
perl の配列とメモリー: 国民宿舎はらぺこ 大浴場面白いな、と思ったのは、上記リンク先の話題を手元で試していたときに、@data = map { rand 10 } (1..1e7); $sum += $_ for @data;だとメモリーを喰いまくるのに、$sum += rand 10 for 1..1e7;だとほとんどメモリーを喰わないこと。
実は、foreach($from..$to)
は、Perl 5.005以来最適化されています。
foreach (1..1000000) optimized foreach (1..1000000) is now optimized into a counting loop. It does not try to allocate a 1000000-size list anymore.
さすがにこれだけ前のversionだと、日本語訳もあります。
ただし残念なことに、これは foreach
のみの特権で、他の場合は相変わらず巨大配列を作ってしまいます。
Perl 6では、遅延評価、すなわち必要な時にだけ要素が生成されるということがサポートされているので、Haskellのように無限リストも扱えるようになる予定です。
が、同様のことはPerl 5でも実は簡単に出来てしまいます。
sub make_range { my ( $now, $end ) = @_; return sub { $now <= $end ? $now++ : undef } } my $sum = 0; my $r = make_range(1, 1e7); $sum += $_ while(defined($_ = $r->())); # definedでくくらないと、0で抜けてしまう print "$sum\n"
ただし、Nativeでないので特別扱いされたforよりもかなり遅いのですが。
% /usr/bin/time perl make_range.pl 50000005000000 9.89 real 9.81 user 0.03 sys % /usr/bin/time perl -le '$sum+=$_ for(1..1e7);print $sum' 50000005000000 2.12 real 2.10 user 0.00 sys
その代わり、このやり方はずっと柔軟です。例えば、逆順も行けます。
sub make_range { my ( $now, $end, $step ) = @_; sub { abs($end - $now) >= 1 ? $now += $step : undef } } my $sum = 0; my $r = make_range(100, 0, -2); $\="\n"; print while(defined($_ = $r->()));
こういったものを、IteratorとかGeneratorとかと呼びます。見ての通り、ClosureがあればPerlに限らず簡単に実装できるのです。
しかし、Tieという仕組みでこれを全く見えなくできてしまうのはPerlならではの醍醐味でもあります。例えば、こんなことも。
このように、IteratorとしてだけArrayを使うのであれば、上記の3つのメソッドを定義するだけでOKです。
{ package Tie::Iterator::Simple; sub TIEARRAY{ my ($pkg, $start, $end) = @_; bless { start => $start, end => $end }, $pkg; } sub FETCH{ my ($this, $index) = @_; $this->{start} + $index; } sub FETCHSIZE{ my ($this, $index) = @_; $this->{end} - $this->{start} + 1; } } tie my @iter, 'Tie::Iterator::Simple', 1, 1e6; # 一桁少ない数で実験 my $sum = 0; $sum += $_ for(@iter); print "$sum\n";
もっとも、スピードは早くありませんが。
% /usr/bin/time perl tie_iter.pl 500000500000 7.58 real 7.47 user 0.03 sys % /usr/bin/time perl -le '$sum+=$_ for(1..1e6);print $sum' 500000500000 0.21 real 0.20 user 0.00 sys
Perlは他の言語からさまざまな機能を盗んでいますが、このTieは他に見当たらないユニークな特徴と言えるでしょう。
Dan the Tied to Perl
追記:
www.textfile.org - perl - for(1..1e10) と Iteratorところで細かい話ですが、bless { start => $start, end => $end }のところは、bless { start => $start, end => $end }, $pkg;のほうがよい? まあ、サブクラス化しなければよいのか。
直しました。まあ上記のような「使い捨て」クラスの場合はこれでもよさげですが、better practiceということで。いつもありがとうございます。
# コメントにまでチェック。ありがとうございました。
Dan the Typo Generator