关于Perl隐藏特性的问题产生了至少一个可以被视为特性或MIS特性的响应。跟进这个问题似乎合乎逻辑:Perl中常见的非明显错误是什么?看起来应该起作用的东西,但不应该起作用。
我不会给出关于如何构建答案的指导方针,也不会给出"太容易"而被认为是正确答案的指导方针,因为这就是投票的目的。
答案表
句法
- 一般
- 标识符中的单引号而不是::。
- 间接对象语法
- 将引用与普通var类型混淆
- 文件句柄
- 使用带词汇文件句柄的print时使用HereDoc符号
- 打印到哈希中包含的词汇文件句柄
- my声明应在变量列表周围使用parens
- 将字符串与==和!=
语义/语言特征
- 一般
- do不是循环。你不能这样做。
- 在regex中使用/o修饰符
- 忘了readdir的结果与CWD无关
- 一元减号与字符串的相互作用
- 语境
- 从数组到列表的标量赋值
- glob()迭代器(在另一个问题上)
- 列表上下文中的隐式返回
- 括号改变了运算符的语义
- 调用上下文被传播到函数中的返回语句
- 变量
- 如果不导出整个typeglob,则无法本地化导出的变量
- 使用具有相同名称的多个变量(不同类型)
- while 不自动本地化$_。
- 有效零的变量
- 常量可以重新定义
调试
最佳实践
- 忘记了use strict和use warnings(或use diagnostics)
- 变量名拼写错误(即,use strict再次出现)
元解答
另请参见:asp.net-通用gotchas
- 不要只是从另一个问题中重新发布答案,但也许您可以将其添加到您的答案表中:stackoverflow.com/questions/146329/…
- 也许有人能解释为什么这是一个gotcha:stackoverflow.com/questions/163026/…
可以使用单引号替换标识符中的::。
考虑:
1 2 3
| use strict ;
print"$foo"; #-- Won't compile under use strict
print"$foo's fun!"; #-- Compiles just fine, refers to $foo::s |
导致以下问题:
1 2 3 4 5
| use strict ;
my $name ="John";
print"$name's name is '$name'";
# prints:
# name is 'John' |
建议避免这种情况的方法是在变量名周围使用大括号:
1 2
| print"${name}'s name is '$name'";
# John's name is 'John' |
还有use warnings,因为它会告诉你未定义变量$name::s的用法。
- 当然,这是Perl4的遗留问题,对于向后兼容是必要的。
- 你说得对,乔纳森,但现在是时候使用一个杂注来启用这个所谓的特性了。Perl4包方法类似于拉丁语。在当今这个时代支持他们,与其说是一种利益,不如说是一种权宜之计。
- 这是一个功能,如果你想用克林贡语写标识符。我同意,否则就太糟糕了。
- 太疯狂了,我从来没有遇到过这种情况,而且经常使用撇号。在这个问题上学到很多!
您可以打印到词汇文件句柄:好。
1 2
| print $out"hello, world
"; |
然后您会意识到拥有一个文件句柄的散列值可能会很好:
1 2 3
| 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: $!"; |
到目前为止,一切都很好。现在尝试使用它们,并根据条件打印到其中一个或另一个:
1 2 3 4
| my $where = (frobnitz () == 10) ? 'ok' : 'fail';
print $out{$where}"it worked!
"; # it didn't: compile time error |
您必须将散列引用包装成一对卷发:
1 2
| print {$out{$where}}"it worked!
"; # now it did |
这完全是不直观的行为。如果你没有听说这件事,或者在文档中读到这件事,我怀疑你能否自己解决。
- 我也被这个咬了…如你所说,高度不可发现。
- print $out"text"可与print $out,"text"混淆。print {$out}"text"不会。
这是一个元答案。很多讨厌的gotcha都被perl::critical捕获,您可以使用perlcritic命令从命令行安装和运行,或者(如果您愿意在Internet上发送代码,但无法自定义您的选项)通过perl::critical网站。
Perl::Critic还提供了Damian Conways Perl最佳实践手册的参考资料,包括页码。所以,如果你太懒了,不愿意读整本书,那么Perl::Critic仍然可以告诉你应该读的内容。
- 当然是为了"应该"的价值。
- 评论家是否有评分,例如5级?
Perl的dwimmer在将print与词汇文件句柄一起使用时,难以使用EDOCX1(本文中的文档)符号:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| # 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 |
解决方案是小心地在filehandle和<<之间包含空白,或者用{}大括号将filehandle包装以消除其歧义:
1 2 3
| print {$fh}<<EOT;
foo
EOT |
PerlTrap手册页列出了许多按类型组织的不小心的陷阱。
最常见的方法是用不同于
1 2
| use strict;
use diagnostics; |
PJF补充道:请注意,诊断对性能有重大影响。它会减慢程序的启动速度,因为它需要加载perldiag.pod,而且直到几个星期前的bleadperl,它也会减慢并膨胀regexps,因为它使用了$&;。建议使用警告并对结果运行splain。
- S/诊断/警告/
- 我更喜欢诊断
- 请注意,诊断对性能有重大影响。它会减慢程序的启动速度,因为它需要加载perldiag.pod,而且直到几个星期前的bleadperl,它也会减慢并膨胀regexps,因为它使用了$&;。建议使用警告并对结果运行splain。
混淆引用和实际对象:
1 2
| $a = [1,2,3,4];
print $a[0]; |
(最好是$a->[0]、$$a[0]、@{$a}[0]或@$a[0]中的一个)
- $A->[0]更干净(并且避免了不必要的一个元素切片)
- 我本以为$A->[0]
- 哇,我从来没有完全理解这一点,我写Perl已经一年多了。我知道->修复了它,但我不知道为什么。这完全有道理!
- 我总是忘了最好的方法——)
- 这一点立即被发现为"使用严格;"
- 不完全是这样,它抱怨var@a没有被导入,如果你不知道这个问题的话,这会让你像以前一样毫无头绪。您可以在前面声明一个@a数组,这会更加混乱。另外,请看我对线程的其他贡献:)
- 我写了$$A[0],一个比$A->[0]短的字符。
- vinko:它还抱怨"global symbol@a需要显式的包名",这是常见的错误。它不会抱怨$A,因为它是自动神奇导入的,但是如果你说"我的$A",那么你只会得到"全局符号…@A"错误。使用$A和@A的人应该得到他们应得的东西:—)
- …这应该是"在同一范围内故意使用$A和@A的人"应该得到他们应得的东西:—)
- "一个元素片"是什么?速度慢吗?它适用于$A[0](我觉得更合适)吗?
- @基辅:IIRC,不,$$A[0]是一种直接的尊重。@$A[0]或@$A[0]是一个元素片,因为它们首先获取引用并取出整个数组(使用@),然后大括号[]被认为是该数组的一个切片,恰好是一个元素,而不是一个索引。
- 差异==取消引用:)
- 顺便说一句,我真的不知道它是否慢。切片肯定会慢一些。我不知道Perl是否知道如何优化索引查找中的单个元素切片,或者在什么情况下它可能会尝试。
给scalars分配数组对我来说毫无意义。例如:
1
| $foo = ( 'a', 'b', 'c' ); |
将"c"分配给$foo并丢弃数组的其余部分。这个更奇怪:
1 2
| @foo = ( 'a', 'b', 'c' );
$foo = @foo; |
这看起来应该和第一个例子做同样的事情,但是它将$foo设置为@foo的长度,所以$foo == 3。
- 在第一个例子中,在标量上下文中有逗号操作符(这里没有数组),所以最后一件事就是结束逗号操作符。在第二个例子中,您有一个数组,所以您得到了您描述的行为。
- 另一种说法是,在第一个示例中,您有一个列表,但在第二个示例中,您有一个数组。
- 如果将parens放在最后一个示例中的$foo周围,$foo将变为"a"
- @布莱恩:是的,但是像这样毫无意义的区别让Perl变得如此混乱和烦人。
- 这不是毫无意义的区别,上下文是语言的基本(和有用)部分。它们容易掌握吗?不,但它们不是毫无意义的。你自己在回答中举了一个例子:"x"操作符的两种用法都很有用。你需要建立上下文来得到你想要的。
- @亚当:你说得对,上下文肯定有用。我想这仅仅是因为Perl的上下文思想经常以一种与我自己的直觉相反的方式工作。如果我设计了Perl,将有Dinstinct标量和列表上下文,但没有明显的"数组上下文"。
- 逗号运算符在C中的工作方式相同。
- @随机黑客:Perl中没有"数组上下文"这样的东西。只有不同的标量和列表上下文。Perl的工作方式和您所说的完全一样。
- @海星:我说得很草率,但意思应该很清楚:每当命令式语言的语法接受了X = Y和Z = Y; X = Z两个表达式时,X在这两种情况下的结果应该是一样的。
在串联中使用未初始化的值…
这个让我发疯。您有一个包含许多变量的打印,例如:
1 2
| print"$label: $field1, $field2, $field3
"; |
其中一个变量是UNdef。您认为这是程序中的一个bug——这就是为什么您使用"strict"pragma的原因。也许您的数据库模式在您不期望的字段中允许空值,或者您忘记初始化变量等,但是所有的错误消息都告诉您,在串联(.操作)期间遇到了未初始化的值。如果它告诉您未初始化的变量的名称就好了!
由于某种原因,Perl不想在错误消息中打印变量名,所以您最终会通过设置断点(查看哪个变量是未定义的)或添加代码来跟踪变量名,以检查条件。当它在CGI脚本中只发生了千分之一的时候非常恼人,而且您不能很容易地重新创建它。
- 我不知道这是不是一个Perl5.10的东西,但是变量名会为我打印出来("在串联中使用未初始化的值$blah…","在模式匹配中…",等等)。
- 当然,在那个特定的例子中,您可以查看打印的内容,标点符号会告诉您。例如,"someLabel:1,3"是$field2,这是不死的。(不过,我假设没有空字符串…"而undef也会打印相同的内容。)
- 正如kev指出的那样,这个错误在5.10中更加明显。
"我的"声明应在变量列表周围使用括号
1 2 3 4 5 6 7 8 9 10
| use strict ;
my $a = 1;
mysub ();
print"a is $a
";
sub {
my $b, $a; # Gotcha!
$a = 2;
} |
因为my声明只适用于$b(该行提到$a根本没有做任何事情)。请注意,即使在"使用严格"有效的情况下,也不会发出警告。
添加"use warnings"(或-w标志)可以极大地改进Perl所说的"my"列表周围缺少括号的情况。这表明,正如许多人已经看到的,为什么严格和警告语用总是一个好主意。
Perl的大多数循环操作符(foreach、map、grep自动本地化$_,但while()不自动本地化,这会导致奇怪的远距离操作。
- 智慧之言:确保在主脚本之外的任何内容中(例如在子例程中),在"while()"之前使用"local$"。
- 使用显式变量进行定义测试,尝试:perl-mo=deparse-we'while(my$line=<>)
- @谢谢,修好了。我本可以发誓以前是真的,但现在显然不是了。
- 这看起来应该是一个bug,是否有计划在将来改变它,或者他们只是将其作为另一个"增强"注销?
我做过一次:
1
| my $object = new Some::Random::Class->new; |
我花了很长时间才发现错误。间接方法语法是eEvil。
- 调用->new不是万能的,例如在main::foo中的*sub foo warn",在foo->new中的*sub new warn",在foo->new中的"my$x=foo->new*这两种方法的解决方案都是限定包名的::,即foo::->new和new foo::
- mkv,也许你应该把这个添加到列表中!我通过包"1"得到了"can't locate object method"new"(也许你忘了加载"1"?)"这无疑是一个比我预想的更令人困惑的信息。
1 2 3 4 5
| my $x = <>;
do {
next if $x !~ /TODO\s*[:-]/;
...
} while ( $x ); |
do不是循环。你不能这样做。这是执行一个块的指令。这和
尽管如此,它看起来像是C语言家族的一个结构。
- 是的,而且做的工作环境相当难看。
- 使用带有redo {my $x = <>; ... ; redo if $condition}的裸块
- @埃里克·斯特罗姆:很酷。我不知道我以前是否在Perl中使用过redo。
- @以弗所记:你所说的这对大括号是什么意思?
- @在这里,next适用于形成一个块的内部{},而不是无效的外部do {}。
常量可以重新定义。意外地重新定义常量的一个简单方法是将常量定义为引用。
1 2 3 4 5
| use constant FOO => { bar => 1 };
...
my $hash = FOO;
...
$hash->{bar} = 2; |
现在foo是bar=>2
如果您使用的是mod_perl(至少在1.3中),那么新的foo值将一直保持到刷新模块为止。
- 哎哟!我自己没有用过"使用常数",但如果将来我需要类似的东西,我现在会在自然界中寻找更多的"常数"。
- 如果我没记错的话,在C语言中,常量指针只意味着你不能把它指向任何其他地方——它指向的实际内容可以更改。从这个角度来看,这种行为是相当合理的。
- 在爪哇,EDOCX1和9也是一样的。
这个gotcha在Perl5.10中是固定的-如果你足够幸运地在一个对升级不过敏的地方工作>:-(
我说的是有效零的变量。你知道,在下列句子中会导致意想不到的结果:
1 2
| unless ($x) { ... }
$x ||= do { ... }; |
Perl5.10具有//=或定义的或运算符。
当有效的零是由一些在代码投入生产之前测试中没有考虑到的边缘条件引起时,这是特别阴险的。
- 5.10之前版本的修复程序是"0但正确"的恐怖。如果有人想了解更多信息,网站上有一个关于这个的问题。
- "0但正确"并非在所有情况下都有效(即空字符串)。即使它确实有效,也只有在控制数据源的情况下才有效。
- 对!无论在哪里,我都能看到Perl代码的例子,这些代码会因输入"或0"而失败。
一元减去"foo"产生"-foo":
1
| perl -le 'print -"foo" eq"-foo" ?"true" :"false"' |
这仅在第一个字符与/[_a-zA-Z]/匹配时才有效。如果第一个字符是"-",则将第一个字符更改为"+",如果第一个字符是"+",则将第一个字符更改为"-"。如果第一个字符与/[^-+_a-zA-Z]/匹配,那么它将尝试将字符串转换为数字并否定结果。
1 2 3 4 5 6 7
| 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
' |
上面的代码打印
1 2 3 4 5
| -foo
+foo
-foo
-0
-5 |
这个功能主要是允许人们说
1 2 3 4 5
| my %options = (
-depth => 5,
-width => 2,
-height => 3,
); |
- 哎呀,我不知道一元负号加在弦上有什么魔力。我想知道在Perlop中还隐藏着什么…
在下面的场景中,您希望@包含哪些值?
1 2 3 4 5
| sub foo { }
# empty subroutine called in parameters
bar( foo(),"The second parameter." ) ; |
我希望在酒吧里收到:
1
| undef,"The second parameter." |
但是@仅包含第二个参数,至少在使用Perl5.88进行测试时是这样。
- foo不返回任何内容,即使是undef,因为它没有要计算的表达式。我想这是一个有争议的问题,无论是一个错误还是"按设计"…
- foo()正在列表上下文中运行,它返回一个空列表,该列表已折叠。要看到这一点,请注意"sub-foo return();"的行为方式相同。
- 空列表的折叠是我经常使用的一个功能,它可以无缝、优雅地完成向子例程调用添加参数的任务。您只需将@extra_参数放入其中,如果您在前面的代码中保留了该参数为空,那么它将完全按照您希望的方式执行——绝对没有,就好像它从未存在过一样。
格雷姆·佩罗的回答很好,但它会变得更好!
给定一个在列表上下文中返回好列表的典型函数,您可能会问:在标量上下文中它将返回什么?(我所说的"典型",是指文档没有说明的常见情况,我们假设它没有使用任何wantarray有趣的业务。也许这是你自己写的函数。)
1 2 3 4 5
| 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,每次调用函数的源代码时,调用方的角色最好仔细检查一遍。
- 很好,尤其是在编写自己的函数时。但你处理这件事的建议可能不是最好的。与其读别人的资料来源,不如不要依赖于非法行为。如果您需要一个标量,请先获取列表,然后从中获取您的标量(长度、第一个等)。
- 使您的代码依赖于经验法则和未记录的特性,从而将您的代码与它们的实现结合起来。如果它们的代码改变了语法,您的代码就会中断。只需坚持文档化的行为,以保护代码不受这种情况的影响。这就是所谓的减速联轴节。
- @亚当:我完全同意:人们不应该让自己的代码依赖于非法行为。正如你所说,首先得到完整的清单是处理它的正确方法。我的观点是,如果Perl的设计不同,那么首先获取列表这一不明显的解决方法就不必要了。
比较使用==和!=而不是eq和ne的字符串。例如:
1 2 3 4
| $x ="abc";
if ($x =="abc") {
# do something
} |
而不是:
1 2 3 4
| $x ="abc";
if ($x eq"abc") {
# do something
} |
- 另请参阅我最喜欢的Perl错误故事"Nancy打字"。短篇小说是"Nancy" !="Nancy",即使"Paul" =="John"
使用带有存储在变量中的regex模式的/o修饰符。
指定/o是$pattern不变的承诺。Perl足够聪明,可以识别它是否发生了变化,并有条件地重新编译regex,因此没有足够的理由再使用/o。或者,您可以使用qr//(例如,如果您一心想避免支票)。
- 它足够聪明了……不确定是哪个版本。参见:perlmunks.org/?node_id=256053不使用/o,使用qr//
- 谢谢,更新了。有人应该向Perlop提交一个Doc补丁。(尤其是当他们知道更改的Perl版本时。)
- 我应该链接到perlmunks.org/?node_id=256155,这样就不必搜索整个线程。
那事实呢
1
| @array = split( / /, $string ); |
结果与
1
| @array = split( ' ', $string ); |
如果$string有前导空格?
这可能会让一些人感到惊讶。
除非导出整个typeglob,否则无法本地化导出的变量。
添加额外的括号永远不会改变代码的含义,对吗?对吗?
1 2
| my @x = ("A" x 5 ); # @x contains 1 element,"AAAAA"
my @y = (("A") x 5 ); # @y contains 5 elements:"A","A","A","A","A" |
哦,对了,这是波尔。
编辑:为了更好的衡量,如果在标量上下文中调用x,那么括号就不重要了:
1 2
| my $z = ("A" x 5 ); # $z contains"AAAAA"
my $w = (("A") x 5 ); # $w contains"AAAAA" too |
直观。
- 这是有据可查的。我知道,如果您不熟悉Perl,上下文可能会令人困惑,但它们是学习语言的基础部分。但是,如果你不想学习,没有人会让你。(我希望;)
- 我对环境很满意。为什么x不根据提供给它自己的上下文(la reverse())来决定向其左边参数提供什么上下文?.有文件证明是的,但这并不能阻止它成为一种无缘无故的不一致。
- 我的观点是:额外的非优先级调整子表达式周围的parens不应该改变表达式的语义,否则程序员会害怕使用parens。大多数Perl都同意$expr总是与((($expr))相同——为什么他们必须打破这个有用的规则?
- 我明白你在说什么,但我只是不认为这是个骗局。也就是说,我看不出有人意外地把帕伦斯放进来,让它做一些意想不到的事情。但是,有很多用Perl做出的设计决策对某些人来说是不直观的。这是主观的。
- +1个投票赞成添加到列表中
- 感谢投票。我同意这是主观的。我个人曾被它咬过几次,当我认为一个关于语言语法的安全假设被违反时,它让我变得急躁。
- 顺便说一句,我完全同意Perl中存在一些非直观的设计决策(将文件句柄传递/返回给以前非常可怕的函数,我重新构造了程序以避免这样做)。但是尽管我有抱怨,我还是用Perl做大多数事情…:)
- 这对我来说很有意义。首先,您要重复一个标量。第二步,你重复一个列表。我认为这里的问题更多的是,除了传统的优先级设置角色之外,parens还被用来构造列表。
- 戴夫:是的。我感到遗憾的是,如果函数是在标量上下文中调用的,那么函数中的parens有时会创建列表上下文,有时则不会创建列表上下文。例如,"return(42,43,44);"
如果您愚蠢到这样做,Perl将允许您声明具有相同名称的多个变量:
因为Perl使用sigils来标识上下文而不是变量类型,所以当以后的代码使用变量时,这几乎保证了混淆,特别是当$x是一个引用时:
1 2 3 4 5 6 7 8 9
| $x[0]
$x{key}
$x->[0]
$x->{key}
@x[0,1]
@x{'foo', 'bar'}
@$x[0,1]
@$x{'foo', 'bar'}
... |
- 在短的单镜头数据咀嚼脚本中效果很好,在其他地方效果也很差。
在对这些结果进行测试之前,忘记预先准备指向readdir结果的目录路径。下面是一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #!/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
" 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的文档中提到了这个gotcha,但我认为这仍然是一个非常常见的错误。
- 在这种情况下,我使用File::chdir模块。它允许您以块本地的方式更改工作目录。如果你用local $CWD = $path在某个块中进行迭代,那么你可以用readdir的结果做任何事情,然后当你离开块时,一切都很好。
散列"constructor"只不过是一个列表,而=>脂肪逗号只不过是句法上的糖而已。当将[]arrayref语法与()list语法混淆时,您可能会被咬到:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| my %var = (
("bar","baz"),
fred =>"barney",
foo => (42, 95, 22)
);
# result
{ 'bar' => 'baz',
'95' => 22,
'foo' => 42,
'fred' => 'barney' };
# wanted
{ 'foo' => [ 42, 95, 22 ] } |
- 呵呵?如果你想要'foo' => [42, 95, 22],那么你应该写下……
- 这就是我为什么说"困惑"。为什么一个新手不会混淆列表/数组/哈希引用的()、[]和{}大括号?
变量名拼写错误…有一次,我花了整整一个下午的时间对行为不正确的代码进行故障排除,结果发现变量名的拼写错误,这在Perl中不是错误,而是新变量的声明。
- 这是许多语言中常见的错误。幸运的是,使用strictpragma有助于确保引用现有变量(使用my或our声明)而不是创建新变量。
- 同意,错误不是以"use strict"开头的
- 是的,总是使用复制粘贴。永远不要重新键入。
- @大卫:实际上不是我的脚本,我是在一个预先存在的脚本上做维护工作…他们依赖于没有"严格使用"。如果我必须再做一次,我会先纠正的。
修改for(each)中循环使用的数组,如下所示:
1 2 3 4 5 6 7
| my @array = qw/a b c d e f g h/;
for ( @array ) {
my $val = shift @array;
print $val,"
";
} |
它会变得混乱,不会像你期望的那样做。
将标量视为整数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| $n = 1729;
$s = 0;
$i = 0;
while ($n) {
$s += $n % 10;
$n/=10;
$i ++
}
print"Sum : $s
";
print"Number of iterations : $i
" |
金额:19
迭代次数:327次
理想情况下,它应该只有四次迭代,但是标量不是int,我们得到了一个意外的结果。
- 抱歉,这不是Perl,这是你。尝试一个简单的测试,比如perl -E 'say"false" unless (0.0)',看看0.0确实是假的y。然后在代码中将$n/=10;改为$n=int($n/10);,看看为什么在循环中的某个地方添加print $n ."
"。对不起,并不是所有的错误都可以归咎于语言。