为什么我不能连接并打印函数返回的值?

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不显示任何内容,实际上似乎被中断,因为明确跳过了换行符.为什么行为改变了?

Fab*_*ica 7

原因是函数ref被调用而没有括号,这导致行被错误地解析.

  • ref在内部调用时if,条件明显用括号分隔,这意味着它ref完全清楚它的参数是什么:$book_ref.没有歧义.
  • 相反,在打印结果时,缺少括号意味着Perl将以非预期的方式解析该行:

    1. 首先,它将连接$book_ref"\n".在标量上下文中,$book_ref求值为字符串HASH(0x1cbef70),因此结果是字符串"HASH(0x1cbef70)\n"
    2. 然后,ref将在字符串上调用"HASH(0x1cbef70)\n",生成一个空字符串作为输出:''.
    3. 此时,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一切都按预期工作.

优先问题的解决方案是使用.

当我第一次写作时,if我决定避免括号使代码更具可读性.然后我复制了它并没有注意到问题.我最后浪费了2个小时来找到这个bug.

最好的解决方案似乎是第一个,因为如果你将它复制到另一个地方(如另一个地方print),你可以保证也复制防止问题的括号.对于第二个,我可能不会意识到括号是多么重要,也不会复制它们.第三个只有当你记得你必须总是使用逗号而不是点时才有效,这不是很明显,因此容易出错.所以,虽然它们有效,但我认为它们不太安全.

其他评论也建议使用printf,这需要处理格式说明符或表达式插值,比如print "ref \$book_ref is a ${\ ref $book_ref }\n";,我觉得难以阅读.

底线:始终使用括号.