Perl的常见问题是什么?

时间:2008-10-03 12:50:09

标签: perl

关于Hidden features of Perl的问题至少产生one response,可能被视为特征或错误特征。跟进这个问题似乎合乎逻辑:Perl中常见的非明显错误是什么?似乎他们应该工作的事情,但不要。

我不会就如何构建答案给出指导,或者什么“太容易”被认为是问题,因为这就是投票的目的。

答案表

语法

语义/语言功能

调试

最佳做法

元的答案

另请参阅:ASP.NET - Common gotchas

29 个答案:

答案 0 :(得分:38)

单引号可用于替换标识符中的::

考虑:

use strict;
print "$foo";        #-- Won't compile under use strict
print "$foo's fun!"; #-- Compiles just fine, refers to $foo::s

导致以下问题:

use strict;
my $name = "John";
print "$name's name is '$name'";
# prints:
#  name is 'John'

避免这种情况的推荐方法是在变量名周围使用大括号:

print "${name}'s name is '$name'";
# John's name is 'John'

还有use warnings,因为它会告诉你使用未定义的变量$name::s

答案 1 :(得分:28)

你可以打印到词法文件句柄:好。

print $out "hello, world\n";

然后你意识到拥有文件句柄的哈希可能会很好:

my %out;
open $out{ok},   '>', 'ok.txt' or die "Could not open ok.txt for output: $!";
open $out{fail}, '>', 'fail.txt' or die "Could not open fail.txt for output: $!";

到目前为止,这么好。现在尝试使用它们,并根据条件打印到其中一个:

my $where = (frobnitz() == 10) ? 'ok' : 'fail';

print $out{$where} "it worked!\n"; # it didn't: compile time error

你必须将哈希取消引用包装在一对curlies中:

print {$out{$where}} "it worked!\n"; # now it did

这是完全不直观的行为。如果您没有听说过这个,或者在文档中阅读,我怀疑您可以自己解决这个问题。

答案 2 :(得分:28)

这是一个元答案。很多讨厌的陷阱被Perl::Critic捕获,您可以使用perlcritic命令从命令行安装和运行,或者(如果您很乐意通过Internet发送代码,而不是能够通过Perl::Critic website

自定义您的选项

Perl::Critic还提供了对Damian Conways Perl最佳实践的书籍,包括页码。因此,如果您懒得阅读整本书,Perl::Critic仍然可以告诉您正在阅读的内容。

答案 3 :(得分:18)

在将<<与词法文件句柄结合使用时,Perl的DWIMmer与print(此处为文档)表示法有争议:

# here-doc
print $fh <<EOT;
foo
EOT

# here-doc, no interpolation
print $fh <<'EOT';
foo
EOT

# bitshift, syntax error
# Bareword "EOT" not allowed while "strict subs" in use
print $fh<<EOT;
foo
EOT

# bitshift, fatal error
# Argument "EOT" isn't numeric...
# Can't locate object method "foo" via package "EOT"...
print $fh<<'EOT';
foo
EOT

解决方案是要小心在文件句柄和<<之间包含空格,或者通过将文件句柄包装在{}大括号中来消除歧义:

print {$fh}<<EOT;
foo
EOT

答案 4 :(得分:17)

perltrap联机帮助页列出了按类型组织的粗心的陷阱。

答案 5 :(得分:16)

最常见的问题是使用与

不同的文件启动文件
use strict;
use diagnostics;

pjf补充说:请注意,诊断程序会对性能产生重大影响。它减慢了程序的启动速度,因为它需要加载perldiag.pod,直到几周前的bleadperl,它也会减慢并膨胀regexp,因为它使用$&amp ;.建议使用警告并在结果上运行splain

答案 6 :(得分:15)

混淆引用和实际对象:

$a = [1,2,3,4];
print $a[0];

(应该是$a->[0](最好),$$a[0]@{$a}[0]@$a[0]

之一

答案 7 :(得分:15)

将数组分配给标量对我来说毫无意义。例如:

$foo = ( 'a', 'b', 'c' );

将'c'分配给$ foo并抛出数组的其余部分。这个更怪异:

@foo = ( 'a', 'b', 'c' );
$foo = @foo;

看起来它应该与第一个示例做同样的事情,而是将$foo设置为@foo长度,所以$foo == 3

答案 8 :(得分:15)

在连接中使用未初始化的值...

这个让我发疯。您的打印件包含许多变量,例如:

print "$label: $field1, $field2, $field3\n";

其中一个变量是 undef 。您认为这是程序中的错误 - 这就是您使用“严格”编译指示的原因。也许您的数据库模式在您不期望的字段中允许NULL,或者您忘记初始化变量等。但是所有错误消息都告诉您在串联(.)操作期间遇到未初始化的值。如果它只告诉你未初始化的变量的名称

由于Perl由于某种原因不想在错误消息中打印变量名,因此您最终会通过设置断点(查看哪个变量 undef )来跟踪它,或者添加代码以检查条件。非常讨厌,当它只在CGI脚本中发生一次,而你无法轻易地重新创建它。

答案 9 :(得分:15)

“my”声明应在变量列表

周围使用括号
use strict;
my $a = 1;
mysub();
print "a is $a\n";

sub {
    my $b, $a;   # Gotcha!
    $a = 2;
}

打印 a是2 ,因为my声明只适用于$b(在该行上提及$a根本没有做任何事情) 。请注意,即使“use strict”生效,也会发生这种情况。

添加“使用警告”(或-w标志)可以极大地改善事情,Perl说“em”列表中的圆括号。正如许多人已经知道的那样,为什么严格警告和警告都是一个好主意。

答案 10 :(得分:14)

Perl的大多数循环运算符(foreachmapgrep)会自动本地化$_,但while(<FH>)不会。这可能导致奇怪的远距离行动。

答案 11 :(得分:12)

我做了一次:

my $object = new Some::Random::Class->new;

让我年龄找到错误。间接方法语法是 eeevil

答案 12 :(得分:11)

my $x = <>;
do { 
    next if $x !~ /TODO\s*[:-]/;
    ...
} while ( $x );

do不是循环。你不能next。这是执行块的指令。

与此相同
$inc++ while <>;

尽管它看起来像是C系列语言中的一种结构。

答案 13 :(得分:9)

可以重新定义常量。意外重新定义常量的一种简单方法是将常量定义为参考。

 use constant FOO => { bar => 1 };
 ...
 my $hash = FOO;
 ...
 $hash->{bar} = 2;

现在FOO是{bar =&gt; 2};

如果您使用的是mod_perl(至少在1.3中),新的FOO值将持续存在,直到刷新模块。

答案 14 :(得分:8)

这个问题在Perl 5.10中得到修复 - 如果你有幸在某个对升级过程不过敏的地方工作&gt;: - (

我说的是有效归零的变量。你知道,导致意外结果的那个如下:

unless ($x) { ... }
$x ||= do { ... };

Perl 5.10具有// =或 defined-或运算符。

当有效零点是由某些边缘条件引起的时候,这是特别隐蔽的,这些边缘条件在代码进入生产之前未在测试中考虑过......

答案 15 :(得分:8)

"foo"的一元减号会创建"-foo"

perl -le 'print -"foo" eq "-foo" ? "true" : "false"'

仅当第一个字符与/[_a-zA-Z]/匹配时才有效。如果第一个字符是"-",则它会将第一个字符更改为"+",如果第一个字符为"+",则会将第一个字符更改为"-" 。如果第一个字符与/[^-+_a-zA-Z]/匹配,则它会尝试将字符串转换为数字并取消结果。

perl -le '
    print -"foo";
    print -"-foo";
    print -"+foo";
    print -"\x{e9}"; #e acute is not in the accepted range
    print -"5foo";   #same thing for 5
'

上面的代码打印

-foo
+foo
-foo
-0
-5

此功能主要用于让人们说出像

这样的内容
my %options = (
    -depth  => 5,
    -width  => 2,
    -height => 3,
);

答案 16 :(得分:7)

在以下情况中,您希望 @_ 包含哪些值?

sub foo { } 

# empty subroutine called in parameters

bar( foo(), "The second parameter." ) ;

我希望收到 bar

undef, "The second parameter." 

@_ 仅包含第二个参数,至少在使用perl 5.88进行测试时。

答案 17 :(得分:6)

/o修饰符与存储在变量中的正则表达式一起使用。

m/$pattern/o

指定/o$pattern不会更改的承诺。 Perl非常聪明,能够识别它是否有条件地改变并重新编译正则表达式,所以没有充分的理由再使用/o。或者,您可以使用qr//(例如,如果您痴迷于避免支票)。

答案 18 :(得分:6)

Graeme Perrow's answer很好,但它变得更好!

鉴于一个典型的函数在列表上下文中返回一个很好的列表,你可能会问:它在标量上下文中会返回什么? (“典型”,我的意思是文档没有说的常见情况,我们假设它不使用任何wantarray有趣的业务。也许这是你自己编写的函数。)

sub f { return ('a', 'b', 'c'); }
sub g { my @x = ('a', 'b', 'c'); return @x; }

my $x = f();           # $x is now 'c'
my $y = g();           # $y is now 3

调用函数的上下文会传播到该函数中的return语句。

我想调用者错误地想要一个简单的经验法则来启用有关代码行为的有效推理。你是对的,Perl,每次 通过调用函数的源代码来调用调用者的角色会更好。

答案 19 :(得分:6)

使用==!=代替eqne来比较字符串。例如:

$x = "abc";
if ($x == "abc") {
    # do something
}

而不是:

$x = "abc";
if ($x eq "abc") {
    # do something
}

答案 20 :(得分:6)

这个事实怎么样?
@array = split( / /, $string );

的结果不同
@array = split( ' ', $string );

如果$ string有前导空格?

这可能会让一些人感到意外。

答案 21 :(得分:5)

除非导出整个typeglob,否则无法本地化导出的变量。

答案 22 :(得分:4)

如果你这么做是愚蠢的,Perl会允许你声明多个具有相同名称的变量:

my ($x, @x, %x);

因为Perl使用sigils来识别 context 而不是变量 type ,所以当后来的代码使用变量时,这几乎可以保证混淆,特别是如果$x是参考:

$x[0]
$x{key}
$x->[0]
$x->{key}
@x[0,1]
@x{'foo', 'bar'}
@$x[0,1]
@$x{'foo', 'bar'}
...

答案 23 :(得分:4)

添加额外的括号永远不会更改代码的含义,对吗?正确?

my @x = ( "A"  x 5 );      # @x contains 1 element, "AAAAA"
my @y = (("A") x 5 );      # @y contains 5 elements: "A", "A", "A", "A", "A"

哦,没错,这是Perl。

编辑:好的方法是,如果在标量上下文中调用x,则括号无关紧要:

my $z = ( "A"  x 5 );      # $z contains "AAAAA"
my $w = (("A") x 5 );      # $w contains "AAAAA" too

直观。

答案 24 :(得分:3)

在对这些结果进行测试之前,忘记将目录路径前置到readdir的结果。这是一个例子:

#!/usr/bin/env perl
use strict;
use warnings;

opendir my $dh, '/path/to/directory/of/interest'
  or die "Can't open '/path/to/directory/of/interest for reading: [$!]";

my @files = readdir $dh; # Bad in many cases; see below
# my @files = map { "/path/to/directory/of/interest/$_" } readdir $dh;

closedir $dh or die "Can't close /path/to/directory/of/interest: [$!]";

for my $item (@files) {
  print "File: $item\n" if -f $item;
  # Nothing happens. No files? That's odd...
}

# Scratching head...let's see...
use Data::Dumper;
print Dumper @files;
# Whoops, there it is...

readdir的文档中提到了这个问题,但我认为这仍然是一个非常常见的错误。

答案 25 :(得分:1)

哈希“构造函数”只不过是一个列表,=>脂肪逗号只不过是语法糖。将[] arrayref语法与()列表语法混淆时,您可能会被此感到困惑:

my %var = (
    ("bar", "baz"),
    fred => "barney",
    foo => (42, 95, 22)
);

# result
{ 'bar' => 'baz',
  '95' => 22,
  'foo' => 42,
  'fred' => 'barney' };

# wanted
{ 'foo' => [ 42, 95, 22 ] }

答案 26 :(得分:-1)

拼写错误的变量名称 ...我曾经花了整整一个下午对代码进行故障排除,这些代码行为不正确只是为了找到变量名称的拼写错误,这在Perl中不是错误,而是声明一个新变量。

答案 27 :(得分:-2)

修改你在for(each)中循环的数组,如:

my @array = qw/a b c d e f g h/;

for ( @array ) {
    my $val = shift @array;
    print $val, "\n";
}

它变得困惑,并没有做你期望的事情

答案 28 :(得分:-2)

将标量视为整数:

$n = 1729;
$s = 0;
$i = 0;

while ($n) {
    $s += $n % 10;
    $n/=10;
    $i ++
}

print "Sum : $s\n";
print "Number of iterations : $i\n"

总和:19

迭代次数:327

理想情况下它应该只有四次迭代,但标量不是int,我们得到了意想不到的结果。