【问题标题】:Why can't I concatenate and print the value returned by a function?为什么我不能连接并打印函数返回的值?
【发布时间】:2017-11-17 19:00:59
【问题描述】:

我有一个调用函数的 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

尽管使用了strictwarnings,但既没有错误也没有警告。
ref 的调用完全相同(它是复制和粘贴),但是虽然它在if 条件内正常工作,但print 不显示任何内容,实际上似乎被中断,作为换行符明显跳过。为什么行为会改变?

【问题讨论】:

    标签: perl return-value function-call


    【解决方案1】:

    原因是函数ref调用时不带括号,导致解析错误。

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

      1. 首先它将连接$book_ref"\n"。在标量上下文中,$book_ref 计算为类似HASH(0x1cbef70) 的字符串,因此结果是字符串"HASH(0x1cbef70)\n"
      2. 然后,ref 将在字符串 "HASH(0x1cbef70)\n"producing as output an empty string:'' 上被调用。
      3. 此时,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 ~~
    
    (...)
    

    “函数调用”实际上是一个“命名一元运算符”(一元运算符为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),你保证也会复制防止出现问题的括号。对于第二个,我可能不会意识到括号的重要性并且不会复制它们。第三种方法只有在您记住必须始终使用逗号而不是点时才有效,这并不明显,因此容易出错。因此,尽管它们有效,但我认为它们不太安全。

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

    底线:始终使用括号

    【讨论】:

    • 或使用逗号,print "value is ", (code), "\n"print LIST
    • 或者使用表达式插值:print "ref \$book_ref is a ${\ ref $book_ref }\n"
    猜你喜欢
    • 2016-05-06
    • 2012-01-03
    • 1970-01-01
    • 2015-03-13
    • 2021-12-30
    • 2014-02-12
    • 2019-01-08
    • 2022-10-14
    相关资源
    最近更新 更多