こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

サブルーチン内のサブルーチン定義について

サブルーチン内で定義したサブルーチンで、思い通りにならない挙動で困っています。
'test'を10万回繰り返す文字列の生成を行い、その文字列長を表示する関数を funcA とします。その生成過程では、自分の関数内で宣言した再帰関数 funcB を呼び出します。


#! /usr/local/bin/perl
use strict;
my $time0;

for(my $i=0; $i<10; $i++){
  $time0 = times();
  &funcA();
  print((times() - $time0). "\n"); # funcAに掛かった時間
}

sub funcA {
  my $buffer = '';
  &funcB(1);
  print length($buffer) . " : "; # $buffer の文字列長
  
  sub funcB{
    my $n = shift;
    $buffer .= 'test';
    return if($n==100000);
    funcB($n+1);
  }
}

この結果が、
400000 :3.063
0 :0.468
0 :0.594
0 :0.766
0 :0.859
0 :1.11
0 :1.187
0 :1.141
0 :1.343
0 :1.469
となり、初回以降 $buffer の長さが0となるのも不可解ですが、funcA の実行時間が増加していくのも理解できません. これを

#! /usr/local/bin/perl
use strict;
my $time0;
my $buffer; # 注1 $buffer をファイル内大域変数として宣言

for(my $i=0; $i<10; $i++){
  $time0 = times();
  &funcA();
  print((times() - $time0). "\n");
}

sub funcA {
  $buffer = ''; # 注2 レキシカル変数宣言をやめた
  &funcB(1);
  print length($buffer) . " : ";
  
  sub funcB{
    my $n = shift;
    $buffer .= 'test';
    return if($n==100000);
    funcB($n+1);
  }
}

とすると、結果は
400004 :3.188
400004 :0.234
[以降、上にほぼ同じ]
と文字列長は正しいものの,初回以降のfuncA実行時間が極端に減ります.
内部ではどういうことが起こっているのでしょうか.

投稿日時 - 2005-10-08 15:57:49

QNo.1700170

困ってます

質問者が選んだベストアンサー

funcAの変数$bufferは、my宣言が実行されるたびに初期化されますが、funcBに現れている$bufferは、その外側のfuncAの1回目の呼び出し時に初期化されたものが保持されます。

ドキュメントperlsubの「永続的なプライベート変数」が参考になるかと。

参考URL:http://www.kt.rim.or.jp/~kbk/perl5.005/perlsub.html

投稿日時 - 2005-10-08 23:21:16

お礼

お返事遅れてしまい、もうしわけありません。
いただいた参考URL、大変勉強になりました。
ありがとうございます。

投稿日時 - 2005-10-12 01:49:14

ANo.3

このQ&Aは役に立ちましたか?

1人が「このQ&Aが役に立った」と投票しています

回答(3)

ANo.2

No.1 です。前半部の方では、やはり意図しない現象が起こっているようです。

funcB のreturn 文の前に、下の1行を加えてみました。

print length($buffer)." , " if($n==100000);

実行結果は下記のとおりです(Win XP の Active Perl 5.8.4)

400000 , 400000 : 5.908
800000 , 0 : 0.621
1200000 , 0 : 0.951
1600000 , 0 : 1.152
2000000 , 0 : 1.392
2400000 , 0 : 1.482
2800000 , 0 : 1.753
3200000 , 0 : 1.832
3600000 , 0 : 2.033
4000000 , 0 : 2.183

どうも、funcB の中の $buffer が、1回目終了時以降、funcB の外(funcA 内)の $buffer と別のものになっちゃってるみたいですね。
(ループを10万回でなく10回とかにしても同様でした。)

結局のところ不可解です。詳しい方の解説を待ちたいと思います。

投稿日時 - 2005-10-08 18:25:08

お礼

上の方の参考URLの「永続的なプライベート変数 」が参考になりました.
いろいろ試していただいてありがとうございますm(_ _)m
perlは奥が深いですねぇ

投稿日時 - 2005-10-12 01:51:05

ANo.1

とりあえず後半部についてですが。

1回目は、$bufferに使うメモリ(最大40万字分)を、文字数が増えるたびに割り当てし直すので、時間がかかる。
2回目以降は、1回目で最終的に割り当てられた40万字分をそのまま使うので、時間がかからない。

ということではないかと思います。
$buffer のファイル内大域変数宣言を、

my $buffer = ' ' x 400000; # 注1...

として(あらかじめ40万字分割り当てておく)みると、1回目の結果が大幅に改善しました。
(それでも2回目以降の2~3倍の時間がかかるので、他にも理由はあるのでしょうけど)

投稿日時 - 2005-10-08 17:52:34

補足

ありがとうございます.
後半についてはちょっとすっきりしました。
そういえば昔に#1さんのおっしゃることを、
http://mikeneko.creator.club.ne.jp/~lab/perl/tuning/#h1

のページで見ていました。でも、すっかり忘れていました.
>my $buffer = ' ' x 400000;
という表記方法があるんですね。2~3倍かかるのも謎ですが、
大変勉強になりました。

ありがとうございます。

投稿日時 - 2005-10-08 18:19:29

あなたにオススメの質問