ハッシュかハッシュリファレンスを渡した直後の挙動の違い
最近は仕事がらみでJavaを触る事が多かったのですが、久しぶりに家でPerlをやっていてちょっとよくわからないことがあります。
事象としてはYAML::SyckのDumpFileにハッシュリファレンスを渡すと、その直後の処理でそのハッシュを一度だけ使えなくなってしまうという事象なんですが、単にYAML::Syckの問題なのかどうか不明です。
[webmaster@localhost script]$ perl -MYAML::Syck -e 'print $YAML::Syck::VERSION',$/ 1.05 [webmaster@localhost script]$
YAML::Syckのバージョンは上記の通り、現時点(2009/4/4)最新の1.05です。
#!/usr/bin/perl use strict; use warnings; use YAML::Syck; my $yml_obj = LoadFile './input.yml'; print 'before DumpFile',$/; &print_all($yml_obj); # ハッシュを渡す DumpFile './outout.yml', %{$yml_obj} or croak $!; # ハッシュリファレンスを渡す #DumpFile './outout.yml', $yml_obj or croak $!; print 'after DumpFile 1st',$/; &print_all($yml_obj); # ハッシュリファレンスを渡した直後だと%$yml_objが認識されない print 'after DumpFile 2nd',$/; &print_all($yml_obj); sub print_all() { my $yml_obj = shift; while( my($key,$value) = each %{$yml_obj} ) { print $key,':',$value,$/; } }
上記のようなサンプルを実行するとハッシュを渡したときは以下のような出力になりますが
[webmaster@localhost 20090404_YAML::Syck]$ ./sample.pl before DumpFile place:tokyo name:seratch after DumpFile 1st place:tokyo name:seratch after DumpFile 2nd place:tokyo name:seratch [webmaster@localhost 20090404_YAML::Syck]$
ハッシュリファレンスを渡したときは以下の通り、直後の処理でハッシュの項目をなめるループに入れません。
[webmaster@localhost 20090404_YAML::Syck]$ ./sample.pl before DumpFile place:tokyo name:seratch after DumpFile 1st after DumpFile 2nd place:tokyo name:seratch [webmaster@localhost 20090404_YAML::Syck]$
デバッガを使って、違いを見てみると
DB<1> v 104 } 105 } 106 else { 107==> local *FH; 108: open FH, "> $file" or die "Cannot write to $file: $!"; 109: if ($#_) { 110: print FH YAML::Syck::DumpYAML($_) for @_; 111 } 112 else { 113: print FH YAML::Syck::DumpYAML($_[0]); DB<1> x @_ 0 HASH(0x9b4ebe8) 'name' => 'seratch' 'place' => 'tokyo' DB<2> YAML::Syck::DumpFile(/usr/lib/perl5/site_perl/5.8.5/i386-linux-thread-multi/YAML/Syck.pm:108): 108: open FH, "> $file" or die "Cannot write to $file: $!"; DB<2> YAML::Syck::DumpFile(/usr/lib/perl5/site_perl/5.8.5/i386-linux-thread-multi/YAML/Syck.pm:109): 109: if ($#_) { DB<2> YAML::Syck::DumpFile(/usr/lib/perl5/site_perl/5.8.5/i386-linux-thread-multi/YAML/Syck.pm:113): 113: print FH YAML::Syck::DumpYAML($_[0]); DB<2> v 110: print FH YAML::Syck::DumpYAML($_) for @_; 111 } 112 else { 113==> print FH YAML::Syck::DumpYAML($_[0]); 114 } 115: close FH; 116 } 117 } 118 119 sub LoadFile { DB<2>
このようにハッシュリファレンスを渡した場合は配列の個数が1($#_は0を返す)なのでelse節に入っていきます。
この場合だと以下のようにループに入れない挙動になります。
DB<2> after DumpFile 1st main::(sample.pl:17): &print_all($yml_obj); # ハッシュリファレンスを渡した直後だと%$yml_objが認識されない DB<2> main::print_all(sample.pl:22): my $yml_obj = shift; DB<2> main::print_all(sample.pl:23): while( my($key,$value) = each %{$yml_obj} ) { DB<2> main::print_all(sample.pl:23): while( my($key,$value) = each %{$yml_obj} ) { DB<2> main::(sample.pl:18): print 'after DumpFile 2nd',$/; DB<2>
デバッガで変数の中身を参照してから処理させてみると$yml_objを認識できるみたいです。
DB<1> after DumpFile 1st main::(sample.pl:17): &print_all($yml_obj); # ハッシュリファレンスを渡した直後だと%$yml_objが認識されない DB<1> x $yml_obj 0 HASH(0x93f1258) 'name' => 'seratch' 'place' => 'tokyo' DB<2> main::print_all(sample.pl:22): my $yml_obj = shift; DB<2> main::print_all(sample.pl:23): while( my($key,$value) = each %{$yml_obj} ) { DB<2> main::print_all(sample.pl:23): while( my($key,$value) = each %{$yml_obj} ) { DB<2> main::print_all(sample.pl:24): print $key,':',$value,$/; DB<2> place:tokyo main::print_all(sample.pl:23): while( my($key,$value) = each %{$yml_obj} ) { DB<2> main::print_all(sample.pl:24): print $key,':',$value,$/; DB<2> name:seratch main::print_all(sample.pl:23): while( my($key,$value) = each %{$yml_obj} ) { DB<2> main::(sample.pl:18): print 'after DumpFile 2nd',$/;
別にデバッガを使わなくても処理の直前に一度参照すると期待通りの挙動になります。
$yml_obj; &print_all($yml_obj); # ハッシュリファレンスを渡した直後だと%$yml_objが認識されない
現状、分かっているのはここまでです。継続して調査して何か分かれば追記します。
(4/9追記)
print FH YAML::Syck::DumpYAML($_[0]);
の処理を通るとこの事象になるのは分かっているのですが、xsをよく知らないので何がどうなのか不明な状態です・・・