【问题标题】:How to create static variable in perl so that I can access in from another script如何在 perl 中创建静态变量,以便我可以从另一个脚本访问
【发布时间】:2020-06-09 19:56:08
【问题描述】:

我有一个脚本(A.pl)和一个包(B.pm),我想在B.pm 中创建一个静态变量,以便A.pl 可以访问它。

A.pl

use lib 'path/to/B_Package';

for loop 10 times {
  fun(age);
}
if ($count>0) {
  print "script fails";
}

B.pm

package B {
  fun() {
    my $age_given = shift;
    my $count;

    eval {
      result = someFileHandling;
    } or die {
      $count++; 
    }
  }
}

【问题讨论】:

  • 您想从A.pl检查模块B中$count变量的值吗?如果您在包范围内将变量声明为 our 而不是函数内的 my ,它将是可访问的
  • 提示:有一个模块叫B。为您的模块使用不同的名称。

标签: perl


【解决方案1】:

我会质疑这样的设计,下面提供了一些替代方案。

但是可以做到——声明为our 的变量可以通过其完全限定名访问。

在包文件Pack.pm

package Pack;    
use warnings;
use strict;

use Exporter qw(import);
our @EXPORT_OK = qw(func);

our $count = 7;

sub func { ++$count }

1;

在它的用户中

use warnings;
use strict;
use feature 'say';

use Pack qw(func);

for (1..2) {
    func();
    say "Current value of a global in 'Pack': ", $Pack::count;
}

$Pack::count = 123;
say "Current value of a global in 'Pack': ", $Pack::count;

所以在调用程序中可以看到在Pack::func() 中对$count 所做的更改。更重要的是,$Pack::count 可以直接由解释器中的任何代码编写。

打算直接使用的包全局变量,如上面的$count 是棘手的生物,很难合理使用,但很容易最终被滥用。

一般而言,您不希望它们:它们的使用与将软件划分为通过明确定义的接口进行通信的组件中的关键思想背道而驰,它们引入了不受控制的耦合,从而破坏了范围等。使用这些变量时,不同的组件在代码中纠缠不清。

但它们当然很有用,并且在库中使用,主要用于常量和参数。

现在,让它们也改变吗?这可能会失控,尽管 也用于库中(通过设置参数来控制它们的行为),但它更接近于“上帝类”的类似物,一个全——控制实体。那时我会直截了当地称其为有缺陷和麻烦制造者。

为什么不让 subs 处理计数并返回更新的值?例如,他们可以使用 state pragma 保留值。或者甚至使用文件范围的变量,只要它是其业务内部的,并且不允许外人戳它。

这是修订后的 Pack.pm

中提到的两种方法的示例
package Pack;    
use warnings;
use strict;
use feature qw(state);

use Exporter qw(import);
our @EXPORT_OK = qw(count func1 func2);

my $count = 7;

sub func1 { ++$count }  # sets counter while doing its other work

sub count {                   # add check that input is numeric
    $count += shift  for @_;  # Set count if values passed,
    return $count;            # return value in either case
}

sub func2 {                       
    state $count = 0;             # keeps count (its own $count)
    return $count += shift // 1;  # demo: add some and return
}

1;

使用演示:

use warnings;
use strict;
use feature 'say'; 

use Pack qw(count func1 func2);

say "Call func2(), using 'state' feature, with its own counter: ";
for (1..2) { say "func2($_): ", func2($_) }
say '';

say "Set value for a file-wide counter, retrieve using count(): ";
for (1..2) { func1() }    
say "Count is: ", count();

say "Use count() to set values as well: ";
for (1..2) {  say "For #$_: ", count($_) }

打印出来

调用 func2(),使用“状态”功能,并带有自己的计数器: 函数2(1):1 函数2(2):3 为文件范围的计数器设置值,使用 count() 检索: 计数是:9 也可以使用 count() 来设置值: 1:10 2:12

下一步是把它变成一个类,然后你就可以用非常自然的方式实现任何类型的计数器了。

有关变量的更多信息,请参阅this postthis post 以及此Effective Perler article,供初学者参考。


严格来说,our 变量不是全局变量,而是一个别名为包变量的词法(“真正的”全局变量!)同名。

【讨论】:

    【解决方案2】:

    我认为有更好的方法来做我猜你想做的事情。我认为你想尝试一定次数的东西,如果你不能实现那个目标就放弃。

    当您调用您的子程序时,您希望它知道尝试了多少次。此外,您想知道它何时失败。

    您不需要为此共享变量。 die 会为您解决这个问题。尽可能多次调用 sub,每次你没有从 eval 得到值时,都算作一个错误:

    my $errors = 0;
    foreach ( 1 .. 10 ) {
        my $result = eval { do_this($age) };
        $errors++ unless defined $result;
        }
    
    print "Script fails" if $errors > 0;
    

    在子例程中,您无需担心此操作执行了多少次,因为这对您来说是在更高级别上发生的。您查看子程序的结果以确定它是否失败并在更高级别调整计数器。现在子程序可以集中注意力,它只是一小部分,而不是考虑为什么要调用它。您也不需要此级别的eval,因为您已经在更高级别拥有它。

    sub do_this {
        my( $age ) = @_;
        ... some file handling ...
        }
    

    工厂

    但是让我们说一个较低的子程序知道它的计数是有充分理由的。我不想污染每个人的那个子程序——假设程序中的其他 10 个地方也调用了这个子程序并且它们都失败了。这应该算入你的电话吗?可能不是。你可能不想要那个。但是,有办法解决这个问题。您可以在需要时创建新版本的子例程。 factory 是制作其他子程序的子程序。

    假设您想尝试某件事一定次数。但是,您可能也想多次这样做。每次你想尝试这个时,都要创建一个新的子程序。告诉该子例程它尝试了多少次:

    sub some_factory {
        my( $max_tries ) = @_;
    
        sub anon_thingy {
            my( $age ) = @_;
            for ( 1 .. $max_tries ) {
                ... file handling ... or die ...
                }
            }
        }
    

    您的程序将如下所示:

    my $try_this = some_factory( 10 );
    
    my $result = eval { $try_this->($age) };
    print "Script fails" unless defined $result;
    

    在同一个程序中,你可以再做一次,每个生成的代码引用都会跟踪自己的使用,不会打扰其他子:

    foreach $age ( list_of_ages() ) {
        my $new_sub = some_factory( 10 );
        my $result = eval { $new_sub->($age) };
        print "Script fails" unless defined $result;
        }
    

    我在Intermediate PerlMastering Perl 上花了很多时间在这类东西上。

    【讨论】:

      猜你喜欢
      • 2019-10-17
      • 1970-01-01
      • 1970-01-01
      • 2018-05-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-06
      • 2011-03-15
      相关资源
      最近更新 更多