我有一个调用函数的Perl程序,在本例中为ref
,并检查结果。具体来说,我正在测试一个变量是一个哈希引用。在这种情况下,ref
将返回'HASH'
。我测试了它并且它起作用了。
然后我决定记录它,添加print
显示同一个调用的结果,但它无法正常工作。这是一个简化版本:
use strict;
use warnings;
my $book_ref = {};
$book_ref->{'title'} = 'The Lord of the Rings';
if (ref $book_ref eq 'HASH') {
print "ref \$book_ref is a " . ref $book_ref . "\n";
}
print "Program is over\n";
令我惊讶的是,这是输出:
ref $book_ref is a Program is over
尽管使用strict
和warnings
,但既没有错误也没有警告
对ref
的调用完全相同(它是复制和粘贴),但是当它在if
条件内正常工作时,print
不显示任何内容,实际上似乎是中断,因为换行符明显被跳过。为什么行为会改变?
答案 0 :(得分:7)
原因是函数ref
在没有括号的情况下被调用,这导致行被错误地解析。
ref
内调用if
时,条件明确用括号分隔,这意味着ref
非常清楚它的参数是什么:$book_ref
。没有含糊之处。相反,在打印结果时,缺少括号意味着Perl将以非预期的方式解析该行:
$book_ref
和"\n"
。在标量上下文中,$book_ref
求值为HASH(0x1cbef70)
之类的字符串,因此结果为字符串"HASH(0x1cbef70)\n"
ref
将在字符串"HASH(0x1cbef70)\n"
上调用,producing as output an empty string:''
。print
打印空字符串,即什么都没有,然后停在那里。系统会跳过换行符\n
,因为它已被ref
使用,因此print
甚至看不到它。而且没有错误。所有这些都来自Perl's operator precedence:来自表格,
(...)
8. left + - .
9. left << >>
10. nonassoc named unary operators
11. nonassoc < > <= >= lt gt le ge
12. nonassoc == != <=> eq ne cmp ~~
(...)
&#34;函数调用&#34;实际上是一个名为“一元运算符”的#34; (一元运算符为ref
)。因此,第8行的.
运算符的优先级高于第10行的函数调用,这就是print
的结果不是预期结果的原因。另一方面,函数调用的优先级高于eq
(第12行),这就是if
内部一切按预期工作的原因。
优先问题的解决方案是使用。
可能是使用括号来强调函数调用:
print "ref \$book_ref is a " . ref($book_ref) . "\n";
另一个我喜欢较少但仍然有效的方法是使用括号来隔离必须连接的字符串,方法是将开始括号放在ref
之前:
print "ref \$book_ref is a " . (ref $book_ref) . "\n";
zdim在评论中提出的另一种可行方法是使用逗号:
print "ref \$book_ref is a ", ref $book_ref, "\n";
当我第一次写if
时,我决定避免括号使代码更具可读性。然后我复制了它并没有注意到这个问题。我最后浪费了2个小时来找到这个错误。
最好的解决方案似乎是第一个,因为如果你将它复制到另一个地方(如另一个print
),你可以保证也复制防止问题的括号。对于第二个,我可能不会意识到括号是多么重要并且不会复制它们。第三个只有当你记得你必须总是使用逗号而不是点时才有效,这不是很明显,因此容易出错。所以,虽然它们有效,但我认为它们不太安全。
其他评论也建议使用printf
,这需要处理格式说明符,或表达式插值,如print "ref \$book_ref is a ${\ ref $book_ref }\n";
,我觉得难以阅读。
底线:始终使用括号。