在Perl中避免全局变量(“正确”的方式是什么?)

时间:2014-09-11 13:03:31

标签: perl variables global-variables code-organization scoping

在编写Perl代码时,我经常遇到文件顶部有一大堆变量的情况,这些变量充当" global"脚本中的变量。我一直在写这些"全球"所有国会大厦字母中的变量都将它们与其他字母区分开来,但我最近一直在研究一些包含许多模块的非常大的脚本,并且非常希望学习一种最好的方法来最小化使用" global"变量并使我的代码尽可能安全。

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

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和sub3共享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,请使用非常具有描述性,我仍然对使用面向对象编程有些新意见。)

谢谢!

2 个答案:

答案 0 :(得分:5)

为什么你不应该使用全局变量在评论和Sobrique's answer中都有很好的涵义。我想通过使用对象给出一个简单的(人为的) how 示例来避免全局变量:

Knight.pm

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_death

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 manual,从Moose concepts部分开始。在perlootut中列出了Moose的其他几种流行替代品。

答案 1 :(得分:2)

首先你需要理解为什么法令是“你不能使用全局”。这是因为在程序中的任何地方都可以修改某些全局范围,这很容易导致错误 - 例如一个子程序,你不要指望以你不期望的方式篡改某些东西。

还存在“名称空间污染”问题 - 如果全局定义$result,如果其他人使用该名称,应该会发生什么?

这就是为什么你应该尽可能地避免使用全局变量。基于代码中“其他地方”发生的事情,以你不期望的方式工作,很容易得到错误。如果你创建一个自包含函数,我不需要阅读它来弄清楚发生了什么。这意味着传入参数,并提取返回的结果,这是数据移动或更改的唯一方式。

这可能看起来很麻烦 - 因为你是正确的 - 但它也非常重要,因为跟踪一个只能来自几行代码的错误要比那些可能来自的错误要容易得多万线计划中的任何地方。如果sub开始崩溃,请查看传递哈希值或面向对象的perl。