为什么我的Perl子例程不能在调用它的foreach循环中看到变量的值?

Syl*_*via 5 perl foreach scoping

我希望这是直截了当的,我做错了.我在网上看到一些关于"变异自杀"看起来不错的东西,但它是旧版本而且我在5.10.1.

无论如何 - 我声明的变量 - $ RootDirectory - 只是突然失去了价值,我无法弄清楚原因.

这是一个重现问题的脚本.当我在调试模式(perl -d)中运行脚本时,我可以在第21行和第26行打印出$ RootDirectory.但它已经过了第30行.

use strict;
my $RootDirectory; 
my @RootDirectories; 

@RootDirectories = (
   'c:\\P4\\EDW\\PRODEDW\\EDWDM\\main\\db\\'
   ,'c:\\P4\\EDW\\PRODEDW\\EDWADS\\main\\db\\'
   ,'c:\\P4\\EDW\\PRODEDW\\FJE\\main\\db\\'
   );

foreach $RootDirectory (@RootDirectories) { 
   # $RootDirectory = 'c:\\P4\\EDW\\PRODEDW\\EDWDM\\main\\db\\';
   # print ' In foreach ' . $RootDirectory. "\n";
   RunSchema ();
} 

exit(0);

sub RunSchema() { 
   # print ' In RunSchema ' . $RootDirectory. "\n";
   CreateTables ();
} 

sub CreateTables() { 
   # print ' In CreateTables ' . $RootDirectory. "\n";
   SQLExecFolder ('tbl');
} 

sub SQLExecFolder() { 
   print ' In SQLExecFolder ' . $RootDirectory. "\n";       # Variable $RootDirectory value is gone by now
} 
Run Code Online (Sandbox Code Playgroud)

编辑感谢您的所有评论!我想现在我将使用"我们的"关键字,这似乎运作良好 - 感谢内森.还要感谢有关使用警告的工具 - 我想我已经卖掉了那个!

继续让我困惑的是为什么,当我进行调试模式(perl -d)并逐步执行代码时,执行"p $ RootDirectory"我得到了第21行和第26行的预期输出,但没有第30行.第30行的情况有何不同?

另外,我感谢关于将$ RootDirectory作为函数参数传递的最佳实践的评论.我想避免这种情况,因为我之后有很多函数 - 例如RunSchema调用调用SQLExecFolder的CreateTables.所有这些都必须传递相同的参数.在这种情况下它仍然有意义,还是有更好的方法来构建它?

Viv*_*ath 8

内森所说的是对的.除此之外,你为什么不传递价值呢?无论如何,这是更好的做法:

foreach $RootDirectory (@RootDirectories) { 
   # $RootDirectory = 'c:\\P4\\EDW\\PRODEDW\\EDWDM\\main\\db\\';
   # print ' In foreach ' . $RootDirectory. "\n";
   RunSchema ($RootDirectory);
} 

sub SQLExecFolder { 
   my $RootDirectory = shift;
   print ' In SQLExecFolder ' . $RootDirectory. "\n";
} 
Run Code Online (Sandbox Code Playgroud)


Nat*_*man 5

你在$RootDirectory循环中声明为循环变量foreach.据我所知,这意味着它的值被local循环到循环中,并且它的值在循环结束时恢复到其先前的值.

在您的情况下,变量从未被赋值,因此在循环结束时它返回到之前的值undef.

编辑:实际上,问题是$RootDirectory声明的问题my,因此在其他范围内未定义.在的功能RunSchema,CreateTables并且SQLExecFolder该变量是未定义的无论定位,foreach.

如果你想声明的变量strict湖,但希望它是全球性的,声明$RootDirectoryour:

our $RootDirectory;
Run Code Online (Sandbox Code Playgroud)

编辑:话虽如此,使用全局变量并不总是一个好主意.最好将变量作为参数传递给其他人建议的函数.

  • 并且`$ RoodDirectory`应该作为函数参数传递 (2认同)
  • 这些函数与原始`$ RootDirectory`位于相同的词法范围内.问题是当循环变量是词法时,`foreach`会创建一个**新的**词法变量.见http://perldoc.perl.org/perlsub.html#Private-Variables-via-my() (2认同)
  • 避免使用全局变量的诱惑. (2认同)

too*_*lic 5

其他人已经正确回答了您的问题。我只想强调您应该添加use warnings;到您的代码中。它会为您的问题提供线索,并提醒您注意另一个潜在的危险。