为什么Perl中的某些函数必须用parens调用而其他函数不调用?

时间:2011-07-22 03:51:29

标签: perl

举例说明的是我自己Test::Version的概要。

use Test::More;
use Test::Version 0.04;

# test blib or lib by default
version_all_ok();

done_testing;

我不必在done_testing();上添加括号我可以简单地调用它。但是当我试图调用version_all_ok; 时(请注意:首次尝试Dist :: Zilla :: Plugin :: Test :: Version失败)我收到错误。这是为什么?

更新也许我的例子不如我想象的那么好。我得到的实际错误是

Bareword "version_all_ok" not allowed while "strict subs" in use at t/release-test-version.t line 19.

这是完整的代码

#!/usr/bin/perl

BEGIN {
  unless ($ENV{RELEASE_TESTING}) {
    require Test::More;
    Test::More::plan(skip_all => 'these tests are for release candidate testing');
  }
}

use 5.006;
use strict;
use warnings;
use Test::More;

eval "use Test::Version";
plan skip_all => "Test::Version required for testing versions"
    if $@;

version_all_ok; # of course line 19, and version_all_ok() works here.
done_testing;

以下内容应与从Test::Version 1.0.0提取的相关摘要一起导出。

use parent 'Exporter';
our @EXPORT = qw( version_all_ok ); ## no critic (Modules::ProhibitAutomaticExportation)
our @EXPORT_OK = qw( version_ok );

6 个答案:

答案 0 :(得分:15)

从根本上说,因为Perl需要知道一个裸字意味着一个函数调用,以便将其解析为一个函数调用。 Perl可以通过两种方式学习这个有趣的事实:

  1. 您可能已经像函数调用一样修饰了裸字,在&->之前添加或在(...)之后添加。 Perl会相信你知道你在说什么,并将裸字解析为函数调用,即使它还不知道它将要调用什么函数。

  2. 在Perl尝试解析调用之前,您可能已声明了具有该名称的函数。通常,use - 模块足以确保符号在正确的时间创建;你在Test::Version做错了,这样在编译测试脚本之后才会导出符号。

  3. 在您的代码中,您将use包装在eval内,这有效地将其延迟到执行时间。因此,当Perl尝试编译调用并且它会爆炸时,符号version_all_ok不可用。强制eval编译时应该足以使符号可用:

    BEGIN {
        eval "use Test::Version";
        plan skip_all => "Test::Version required for testing versions"
            if $@;
    }
    

答案 1 :(得分:4)

这个例子显示(显然我认为)您需要的只是预先声明该函数。

#!/usr/bin/env perl

use strict;
use warnings;

sub hi {
  print "hi\n";
}

hi; #could be `hi();`
bye();  #could not be `bye;`

sub bye {
  print "bye\n";
}

如果您的敏感性要求您在底部定义子例程,但是您希望它们可以在没有parens的情况下调用(就像预先声明的那样),那么您可以使用subs编译指示:

#!/usr/bin/env perl

use strict;
use warnings;

use subs qw/hi bye/;

hi;
bye;

sub hi {
  print "hi\n";
}

sub bye {
  print "bye\n";
}

更新: 似乎subs pragma甚至可以缓解字符串错误带来的问题。您可以在脚本顶部附近尝试use subs 'version_all_ok';。我的概念证明:

#!/usr/bin/env perl

use strict;
use warnings;

use subs qw/hi bye/;

eval <<'DECLARE';
sub bye {
  print "bye\n";
}
DECLARE

hi;
bye;

sub hi {
  print "hi\n";
}

答案 2 :(得分:2)

我不能使用Test :: Version 1.0.0或0.04复制它。你有可能没有出口你认为的那样吗?

你能仔细检查并提供失败的完整脚本,错误消息和成功的完整脚本,以及你正在使用的perl版本吗?

更新:好的,你在运行时加载Test :: Version;这意味着在编译时遇到version_all_ok时,没有这样的子例程。如果不以某种方式修改测试脚本,没有任何办法,例如:

my $has_test_version;
BEGIN { $has_test_version = eval "use Test::Version; 1" }
plan skip_all => "Test::Version required for testing versions" if ! $has_test_version;

答案 3 :(得分:0)

只需要在使用之前声明,仍然使用括号或符号来区分和清晰。

答案 4 :(得分:-1)

如果之前已声明该函数,则允许它,并将其视为列表运算符(警告:它可能会更改运算符优先级!)

答案 5 :(得分:-2)

Programming Perl开始,第29章功能

  

预定义的Perl函数可以在其参数周围使用括号或不使用括号;本章中的语法摘要省略了括号。如果使用括号,那么简单但偶尔会出现令人惊讶的规则是:如果它看起来像一个函数,那么它就是一个函数,所以优先级无关紧要。否则,它是列表运算符或一元运算符,优先级很重要。要小心,因为即使在关键字及其左括号之间放置空格,也不会使它成为一个函数

发现于第677页(由于版权而在Google Books失踪) - 每个Perl程序员都应该有骆驼书。