避免在Perl中使用全局变量("right"方法是什么?



在编写Perl代码时,我经常遇到这样的情况:在文件的顶部有一大簇变量,它们充当";全局";脚本中的变量。我一直在写这些";全局";所有大写字母的变量,以将它们与其他变量区分开来,但我最近一直在研究一些具有许多模块的非常大的脚本,我真的很想学习一种将";全局";变量,并使我的代码尽可能安全。

我经常遇到的情况是有两个共享大量变量的子程序:

my $var1, $var2, $var3, $var4, $var5 . . .
sub sub1 {
    # uses $var1, $var2, $var3 . . .
    # may even make changes to some of these variables
}
sub sub2 {
    # also uses $var1, $var2, $var3 . . .
    # may change $var1, $var2, $var3
}
sub sub3 {
    # doesn't use $var1, $var2, $var3
    # still has access to change them
}

将所有这些变量传递到每个子例程并返回4或5个变量可能看起来很难看,而且很快就很难跟踪所有变量,但如果我将这些变量保持为全局变量,我会遇到sub3的问题,可能会在不应该编辑它们的时候对它们进行编辑。

我知道我可以使用";{}";这样做,但在我个人看来,我认为这看起来相当丑陋:

{
    my $var1, $var2, $var3, $var4, $var5 . . .
    sub sub1 {
        # uses $var1, $var2, $var3 . . .
        # may even make changes to some of these variables
    }
    sub sub2 {
        # also uses $var1, $var2, $var3 . . .
        # may change $var1, $var2, $var3
    }
}
sub sub3 {
    # doesn't use $var1, $var2, $var3
    # no longer has access to change them
}

同样,如果我使用";{}";作用域变量如果sub1和sub2共享他们不希望sub3访问的变量,然后我也希望sub1和ub3共享sub2无法访问的变量会发生什么?使用";{}";以确定似乎不可能的变量的范围。尝试这样做看起来像这样:

{
    my $varx, $vary, $varz . . .
    {
        my $var1, $var2, $var3, $var4, $var5 . . .
        sub sub1 {
            # uses $var1, $var2, $var3 . . .
            # may change $var1, $var2, $var3
            # uses $varx, $vary, $varz . . .
            # may change $varx, $vary, $varz
        }
    
        sub sub2 {
            # also uses $var1, $var2, $var3 . . .
            # may change $var1, $var2, $var3
            # does not use $varx, $vary, $varz . . .
            ############################################
            # still able to change $varx, $vary, $varz #
            ############################################
        }
    }
    
    sub sub3 {
        # doesn't use $var1, $var2, $var3
        # no longer has access to change them
        # uses $varx, $vary, $varz . . .
        # may change $varx, $vary, $varz
    }
}

有没有一种更好的方式来限制访问,而不是使用";{}"?即使唯一的解决方案是进入面向对象的Perl,也可以随意抛出一些想法(如果你确实进入了面向对象的Perl,请非常描述性,我对使用面向对象编程还是有些陌生)。

谢谢!

为什么不应该使用全局变量在评论和Sobrique的回答中都有很好的介绍。我想举一个简单的(人为的)例子来说明如何通过使用对象来避免全局:

骑士下午

package Knight;
use Moose;
use namespace::autoclean;
has 'name' => (
    is       => 'rw',
    isa      => 'Str',
    required => 1
);
has 'quest' => (
    is       => 'rw',
    isa      => 'Str',
    required => 1
);
has 'favorite_color' => (
    is       => 'rw',
    isa      => 'Str',
    required => 1
);
__PACKAGE__->meta->make_immutable;

cross_bridge_of_eth

use strict;
use warnings FATAL => 'all';
use 5.010;
use Knight;
use List::Util qw(none);
sub cross_bridge {
    my ($crosser) = @_;
    my @answers = ($crosser->name, $crosser->quest, $crosser->favorite_color);
    my $success = none { /. No / } @answers; # Can't change your answer
    return $success;
}
my $lancelot = Knight->new(
    name           => 'Sir Lancelot of Camelot',
    quest          => 'To seek the Holy Grail',
    favorite_color => 'Blue'
);
my $galahad = Knight->new(
    name           => 'Sir Galahad of Camelot',
    quest          => 'To seek the Holy Grail',
    favorite_color => 'Blue. No yel...'
);
foreach my $knight ($lancelot, $galahad) {
    say $knight->name, ': "Auuuuuuuugh!"' unless cross_bridge $knight;
}

输出:

Sir Galahad of Camelot: "Auuuuuuuugh!"

本例使用Moose,它只是改进Perl本机OO语法的几个模块之一。

现在,这可能看起来像是很多代码,只是为了检查几个字符串。毕竟,我们可以完全去掉Knight类,并更改cross_bridge函数,这样我们就可以这样称呼它:

cross_bridge($name, $quest, $favorite_color);

甚至:

cross_bridge({
    name           => $name,
    quest          => $quest,
    favorite_color => $favorite_color
});

但我们必须跟踪三个变量,而不是一个。使用对象可以通过单个变量访问多个属性,因此可以减少传递给子例程的参数数量。

OO是一个大主题(我甚至还没有谈到方法,它可以进一步简化代码)。我建议阅读perlootut,然后浏览Moose手册,从Moose概念一节开始。perlootut中还列出了驼鹿的其他几种流行替代品。

首先你需要理解为什么法令是"你不应该使用全局变量"。这是因为全局范围的东西可以在程序中的任何地方进行修改,这很容易导致错误——例如,一个你意想不到的子程序以你意想不到的方式篡改了一些东西。

还有一个"名称空间污染"的问题——如果你全局定义$result,如果其他人使用这个名称,会发生什么?

这就是为什么您应该尽可能避免全局变量。基于代码中"其他地方"发生的事情,很容易出现错误,这些错误的工作方式是你意想不到的。如果你创建了一个自包含的函数,我不需要阅读它来了解发生了什么。这意味着传递参数,提取返回的结果,这是数据移动或更改的唯一方式。

这可能看起来很繁重,而且你是正确的,但这也非常重要,因为追踪一个只能来自几行代码的错误比追踪一个可能来自10000行程序中任何地方的错误要容易得多。如果sub开始出现故障,请考虑传递散列或面向对象的perl。

相关内容

  • 没有找到相关文章