Fab*_*ica 1 perl return-value function-call
我有一个调用函数的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";
Run Code Online (Sandbox Code Playgroud)
令我惊讶的是,这是输出:
ref $book_ref is a Program is over
Run Code Online (Sandbox Code Playgroud)
尽管使用strict并且warnings既没有错误也没有警告.
调用ref是完全相同的(它是复制和粘贴),但它在if条件内正常工作,print不显示任何内容,实际上似乎被中断,因为明确跳过了换行符.为什么行为改变了?
原因是函数ref被调用而没有括号,这导致行被错误地解析.
ref在内部调用时if,条件明显用括号分隔,这意味着它ref完全清楚它的参数是什么:$book_ref.没有歧义.相反,在打印结果时,缺少括号意味着Perl将以非预期的方式解析该行:
$book_ref和"\n".在标量上下文中,$book_ref求值为字符串HASH(0x1cbef70),因此结果是字符串"HASH(0x1cbef70)\n"ref将在字符串上调用"HASH(0x1cbef70)\n",生成一个空字符串作为输出:''.print打印空字符串,即什么都没有,然后停在那里.\n跳过换行符,因为它已被消耗ref,所以print甚至没有看到它.而且没有错误.所有这些都来自Perl的运算符优先级:从表中,
(...)
8. left + - .
9. left << >>
10. nonassoc named unary operators
11. nonassoc < > <= >= lt gt le ge
12. nonassoc == != <=> eq ne cmp ~~
(...)
Run Code Online (Sandbox Code Playgroud)
其中"函数调用"实际上是"命名的一元运算符"(一元运算符是ref).因此第.8行的运算符优先于第10行的函数调用,这就是为什么结果print不是预期的结果.另一方面,函数调用的优先级高于eq(在第12行),这就是为什么if一切都按预期工作.
优先问题的解决方案是使用.
可能是使用括号来强调函数调用:
print "ref \$book_ref is a " . ref($book_ref) . "\n";
Run Code Online (Sandbox Code Playgroud)另一个,我喜欢较少,但仍然有效,是使用括号来隔离必须连接的字符串,通过在前面放置开始括号ref:
print "ref \$book_ref is a " . (ref $book_ref) . "\n";
Run Code Online (Sandbox Code Playgroud)zdim在评论中提出的另一种可行方法是使用逗号:
print "ref \$book_ref is a ", ref $book_ref, "\n";
Run Code Online (Sandbox Code Playgroud)当我第一次写作时,if我决定避免括号使代码更具可读性.然后我复制了它并没有注意到问题.我最后浪费了2个小时来找到这个bug.
最好的解决方案似乎是第一个,因为如果你将它复制到另一个地方(如另一个地方print),你可以保证也复制防止问题的括号.对于第二个,我可能不会意识到括号是多么重要,也不会复制它们.第三个只有当你记得你必须总是使用逗号而不是点时才有效,这不是很明显,因此容易出错.所以,虽然它们有效,但我认为它们不太安全.
其他评论也建议使用printf,这需要处理格式说明符或表达式插值,比如print "ref \$book_ref is a ${\ ref $book_ref }\n";,我觉得难以阅读.
底线:始终使用括号.