ハッシュかハッシュリファレンスを渡した直後の挙動の違い

最近は仕事がらみで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をよく知らないので何がどうなのか不明な状態です・・・