2009-05-01 16 views
13

私は多くのデータを集めたPerlスクリプトを持っています。小さく始まるが、ドット(concatentation)演算子の繰り返し使用のために本当に長くなる文字列変数の束があります。この方法で文字列を拡張すると、繰り返し割り当てが行われますか?はいの場合は、文字列を事前に割り当てる方法はありますか?Perlで文字列をどのように事前に割り当てることができますか?

答えて

7

対処がずっと簡単な別の提案:push配列上の文字列と完了時にjoinです。

+7

配列内のすべての要素はすべてのオーバーヘッドを持つSVを作成しますが、あなたはこのように多くのメモリを使い果たします。 –

-2

はい、拡張していることがわかっている拡張文字列は良い考えです。

「x」演算子を使用してこれを行うことができます。例えば、1000のスペース事前に割り当てる:

$ sでの= "" X 1000:

+0

そして、lhsの代入でsubstrを使用します。 Uuuuugly。 – chaos

+0

これは1000文字のスペースを含む文字列を作成しますが、 "$ s = 'foo'"と言うと、最初の3文字のみを使用して1000文字の文字列を取得するか、新しい3文字の文字列を返しますあなたを捨てる? (私は後者だと思っていますが、実際にはどのようにperlがそれを処理するのかは分かりません)。 –

+1

再割り当てすると、古い結果が捨てられます。 Dave氏によると、文字列の一部のみを変更するには、文字列の置換を行う必要があります。 ++ array-then-join – Anonymous

7

Perlの文字列は変更可能ですが、その文字列に追加するとは、文字列の重複ペナルティが発生しませありません。

あなたは「速い」方法を見つけたいすべてを試すことができますが、これは時期尚早の最適化の本当に臭いです。例えば

、私はハードワークを抽象化するクラスを手早く。それは完璧に動作しますが、それはすべての馬鹿馬鹿しいトリックのために、本当に遅いです。ここで

が結果だ:

  Rate magic normal 
magic 1.72/s  -- -93% 
normal 23.9/s 1289%  -- 

はい、そうです、Perlは私が立派な実装だと思ったものよりも1200パーセント高速です。

でも知られている問題ではありませんものを最適化しようとしないで、あなたのコードをプロファイリングし、実際の問題が何であるかを見つけます。

#!/usr/bin/perl 

use strict; 
use warnings; 

{ 

    package MagicString; 
    use Moose; 

    has _buffer => (
     isa => 'Str', 
     is => 'rw', 
    ); 
    has _buffer_size => (
     isa  => 'Int', 
     is  => 'rw', 
     default => 0, 
    ); 
    has step_size => (
     isa  => 'Int', 
     is  => 'rw', 
     default => 32768, 
    ); 
    has _tail_pos => (
     isa  => 'Int', 
     is  => 'rw', 
     default => 0, 
    ); 

    sub BUILD { 
     my $self = shift; 
     $self->_buffer(chr(0) x $self->step_size); 
    } 

    sub value { 
     my $self = shift; 
     return substr($self->{buffer}, 0, $self->{_tail_pos}); 
    } 

    sub append { 
     my $self = shift; 
     my $value = shift; 
     my $L  = length($value); 
     if (($self->{_tail_pos} + $L) > $self->{_buffer_size }){ 
      $self->{buffer} .= (chr(0) x $self->{step_size}); 
      $self->{_buffer_size} += $self->{step_size}; 
     } 
     substr($self->{buffer}, $self->{_tail_pos}, $L, $value); 
     $self->{_tail_pos} += $L; 
    } 
    __PACKAGE__->meta->make_immutable; 
} 


use Benchmark qw(:all :hireswallclock); 

cmpthese(-10 , { 
     magic => sub{ 
      my $x = MagicString->new(); 
      for (1 .. 200001){ 
       $x->append("hello"); 
      } 
      my $y = $x->value(); 
     }, 
     normal =>sub{ 
      my $x = ''; 
      for (1 .. 200001){ 
       $x .= 'hello'; 
      } 
      my $y = $x; 
     } 
    }); 
#use Data::Dumper; 
#print Dumper(length($x->value())); 
+3

Perlは文字列を複製しないと言うと真偽の半分に過ぎません。 Perlは文字列に余分な文字を割り当てるだけなので、Perlは追加時に文字列を含むメモリを増やす可能性が非常に高いでしょう。これにより、メモリがコピーされる可能性があります。しかし、これはあなたのシステムのメモリマネージャで非常に高速です。 O(n)は数学のクラスでO(logn)を打つことを覚えていますが、現実世界ではアルゴリズムの一定時間が重要です。 Cは速いです。 – Schwern

+0

実際、O(1)が1ステップで数日間であればO(1)はあまり良くありませんが、O(n^2)では数秒しかかかりません:) データサイズが非常に大きいO(n^2)アプローチが数週間を超え、そのサイズのデータ​​セットが一般的であることを示しています。 –

15

はい、Perlは文字列を増やすと繰り返し再割り当てされます。 Perlは文字列に余分なスペースを割り当てますが、数バイトしか割り当てません。これはDevel :: Peekを使って見ることができます。この再割り当ては非常に高速であり、しばしば実際にメモリをコピーしません。あなたのメモリマネージャーを信頼してください。それがC言語ではなくPerlでプログラミングしている理由です。まずはベンチマークしてください!

$#array = $num_entriesで配列を事前に割り当て、keys %hash = $num_keysでハッシュを割り当てることはできますが、length $string = $strlenではハッシュが機能しません。ここにはclever trick I dug up on Perlmonksがあります。

my $str = ""; 
vec($str, $length, 8)=0; 
$str = ""; 

また、XSに入る場合は、SvGROW()に電話することができます。配列を使用して、それをすべて一緒に参加する

混沌の提案は倍以上のメモリを使用します。配列のメモリ。配列内の各要素に割り当てられた各スカラーのメモリー。各スカラー要素に保持されている文字列のメモリ。参加時のコピーのメモリ。コードが単純になる場合は実行してください。ただし、メモリを節約しているとは思わないでください。

0

私は、配列に/道に参加行くだろう。いくつかの後の時点でのデバッグのためのすべての要素へのアクセス権を持っている、

push(@array, $crunched_bit) 

そして$str = join('', @array)、より多くの何もない場合。

+0

これは、すべての配列要素が新しいSVを必要とするので、非常に余分なメモリを使い果たします。 –

3

具体的なPerl文字列の実装方法はわかりませんが、それはconstant amortized timeです。つまり、たとえ文字列を事前に割り当てる方法を見つけたとしても、すべてのスクリプトのユーザーのために保存する時間は、スタックオーバーフローでthis questionを要求した時間よりも短くなります。

関連する問題