【问题标题】:Why is Perl saying I only use this once? Why is that even an issue?为什么 Perl 说我只使用一次?为什么这甚至是一个问题?
【发布时间】:2012-02-02 12:00:20
【问题描述】:

我已经设法将一些代码缩小到以下最小代码示例。

首先我有一个模块plugh.pm,它负责读取配置文件。这里面的肉基本上可以换成下面这样设置一个配置项:

use strict;
use warnings;
sub cfgRead () { $main::cfg{"abc"} = "/usr"; }
1;

然后我有一个主程序,它使用该模块如下,只需调用该函数来设置配置项,然后在子例程中使用其中一项:

#!/usr/bin/env perl

use strict;
use warnings;
use 5.005;

require File::Basename;
import File::Basename "dirname";
push (@INC, dirname ($0));
require plugh;

my (%cfg);

sub subOne () {
        my $list = `ls -1 $main::cfg{"abc"}`;
        my @list = split (/\s+/, $list);
        my $fspec;
        foreach $fspec (@list) {
                print $fspec . "\n";
        }
}

sub mainLine () {
        cfgRead();
        subOne();
}

mainLine();

现在,当我运行它时,我得到以下输出,第一行是标准错误,其余的是标准输出:

Name "main::cfg" used only once: possible typo at /home/xyzzy/bin/xyzzy line 15.
bin
games
include
lib
lib64
local
sbin
share
src

它抱怨的那一行是ls -1 子进程的创建。我的问题很简单:那又怎样?是的,我只使用过一次,但为什么这甚至是个问题?

如果我从不使用它,那很好,但我不明白为什么 Perl 警告我只使用它一次。

我从关联数组中获取变量,然后使用它来获取目录列表。是否有某种奇怪的 Perl 准则规定变量必须至少使用两次?七次?四十二?我真的很难过。

【问题讨论】:

  • 为什么要使用全局来传递%cfg 数据?为什么不返回数据,并使用my %cfg = cfgRead();
  • @TLP,主要是因为将它传递给需要在其他模块中访问它的函数很痛苦 :-) 但我可能最终会这样做,因为它被更好地封装(或更有可能的是,根据需要传递各个配置项)。
  • perl -Mdiagnostics -e '$used_only_once=1'

标签: perl


【解决方案1】:

我认为您的原始问题已得到解答,所以我将传递我的建议。如果可以避免的话,永远不要使用全局变量。您将子例程仅用作代码集群,而不向它们传递任何参数,这就是您的问题所在。

模块:

sub cfgRead {
    my %cfg;
    $cfg{"abc"} = "/usr";
    ...
    return \%cfg;
}

主要:

sub subOne {
    my $cfg = shift;
    my $list = `ls -1 $cfg->{"abc"}`;
    ....
}

my $cfg = cfgRead();
subOne($cfg);

【讨论】:

  • 为此+1,因为这是我最终会采用的方式,并进行了适当的封装。
【解决方案2】:

这里有一些奇怪的东西。

首先:当您激活use strict 时,如果您使用变量而不声明它,通过完全限定名称引用它,您将收到警告。

您实际上所做的是在xyzzy.pl 中用my() 声明一个本地%cfg,然后引用一个不同,包全局变量%main::cfg(由使用其完全限定的名称)。

要使引用链接指向您声明的同一 %cfg,您应该将其声明为 our() 以使其成为包全局。然后你可以在两个地方将它引用为$main::cfg{}(或者只是xyzzy.pl中的$cfg{}),或者你也可以在plugh.pm中声明它our()(这样你就可以使用裸露的%cfg在这两个地方)。

奇怪的是你确实有两个对该变量的引用,所以你不应该得到警告。我认为这里发生的情况是两个单独文件中的隐式声明被假定为单独的变量。

xyzzy.pl:

require plugh;

our (%cfg);

sub subOne () {
   my $list = `ls -1 $cfg{"abc"}`;
   ...
}

plugh.pm:

our(%cfg);
sub cfgRead () { $cfg{"abc"} = "/usr"; }

【讨论】:

  • 我想这可能是它虽然我需要做更多的调查。如果我将my (%cfg); 更改为$main::cfg = ();,警告就会消失,其他一切仍然有效。
  • 是的,没错——但它相当于使用our()。一个完全限定的变量名被假定为包全局的。在我看来,完全限定的隐含声明并不真正符合use strict 的精神。
【解决方案3】:

这只是一个有用的评论,因为将数据存储在某个东西中但不再查看它是非常不寻常的。让我们看一个更有帮助的例子:

use warnings;
$something = 1;
$something = $something + 1;

当然,如您所愿,效果很好。但是考虑一个错误:

use warnings;
$something = 1;
$something = $somehting + 1;

如果您不仔细观察,您不会注意到拼写错误,也可能无法弄清楚最终值错误的原因(因为$somehting 实际上是 0)。

在这种情况下,警告:

Name "main::somehting" used only once: possible typo at tmp.pl line 3.

更有用。它显示了一个可能的错字。

(当然use strict;在这里会更好)

【讨论】:

  • 韦斯,这个答案的唯一问题是它比想象的要微妙得多。这不是您假设的变量拼写错误的问题,我在另一个模块中设置的变量与我使用的变量的拼写相同。事实证明,罗素说得对:虽然拼写相同,但所指的是不同的东西。
  • 实际上,我意识到my 是一个问题,并且打算把它也扔在那里。但是您的问题不是“为什么代码没有像我想象的那样工作”,而是“我的问题很简单:那又怎样?是的,我只使用过一次,但为什么这甚至是一个问题?”。实际上,如果我是你,我不会编写引用 $main::anything 的代码。您应该编写它,以便将 \%cfg 传递到模块中的函数或其他东西中,并使用引用。最终它会是更好的代码,并且会更好地将模块与使用隔离开来。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-30
  • 1970-01-01
  • 2016-05-21
  • 2017-05-07
  • 2021-08-14
相关资源
最近更新 更多