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

sr4lhrrt  于 2022-11-15  发布在  Perl
关注(0)|答案(2)|浏览(167)

在编写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个变量看起来非常难看,并且很快就很难跟踪所有变量,但如果我将这些变量保持为全局变量,我会遇到sub 3可能在不应该编辑它们的时候编辑它们的问题。
我知道我可以使用“{}”来限定变量的范围,但我个人认为这看起来很难看:

{
    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和sub 2共享了它们不希望sub 3访问的变量,然后我又希望sub1和sub 3共享sub 2不希望访问的变量,会发生什么情况?使用“{}”来限定变量的范围似乎是不可能的。尝试这样做的结果如下:

{
    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,请非常描述性地说,我对使用面向对象的编程还是有点陌生)。
谢谢你!

t9eec4r0

t9eec4r01#

  • 为什么 * 你不应该使用全局变量在注解和Sobrique's answer中有很好的解释。我想给予一个简单的(人为的)例子,* 如何 * 通过使用对象来避免全局变量:
  • 骑士.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;
  • 跨过死亡之桥 *
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替代品。

gr8qqesn

gr8qqesn2#

首先,你需要理解为什么“你不应该使用全局变量”,这是因为全局变量可以在程序中的任何地方被修改,这很容易导致bug--比如,一个你不希望的子程序以你不希望的方式篡改了一些东西。
还有一个“名称空间污染”的问题--如果全局定义$result,那么如果其他人使用这个名称会发生什么?
这就是为什么你应该尽可能地避免全局变量的原因。在代码的“其他地方”发生的事情很容易导致错误,这些错误以你没有预料到的方式工作。如果你创建了一个自包含函数,我不需要读它来弄清楚发生了什么。这意味着传入参数,并提取返回的结果。并且这是移动或更改数据的唯一方式。
这可能看起来很麻烦--你是对的--但这也是非常重要的,因为跟踪一个bug要比跟踪一个可能来自10,000行程序中任何地方的bug容易得多。如果sub开始崩溃,可以考虑传递哈希或面向对象的perl。

相关问题