Perl 08:返回“字段列表”会让调用位置改写结果
“解析出 api 和 queued 两个字段”若用 return ('api', 'queued') 表达,my @fields = ... 能得到两个值,my $field = ... 却只得到末项 queued。使用者不是换了函数,只换了赋值位置,结果形状已经变了。perlsub 对返回表达式的说明明确指出,它在调用者要求的上下文中求值;perldata 再说明标量位置的列表字面值为何取最后一项。不能把它误说成“数组在标量位置也取最后一项”:数组会给长度。
实验 examples/perl-language/experiments/08-return.pl 的金标是 list=api|queued、scalar=queued、ref=ARRAY、count=2;命令、无 STDERR 和退出码 0 记录在 examples/perl-language/evidence/05-08.md。前两行是一个易用错的反例,后两行用 return ['api','queued'] 将单个引用作为返回契约:接收端须显式解引用才取字段。这是当前 LogTool::read_records 选择数组引用的原因,不是对 Perl 默认返回语义的修改。
还有两条容易混淆的路径:return @array 在标量位置会得到数组长度;return 后不提供表达式,在列表位置为空列表,在标量位置是 undef。因此不能只凭某一次 my $x = ... 的打印结果推测函数所有调用方式。报错时这套工具选择 die 并由 CLI 明确返回状态 2,而不是把空数组引用、空列表与失败暗中混用。记录格式错误时不能发布“已导入零条”的成功回执。
补充闭包实验也给返回契约增加一个形状:make_filter(...) 返回的不是列表,也不是记录,而是一个代码引用。调用者必须写 $filter->($record) 才能得到布尔结果。若误把它放进字符串或数字上下文,只会得到代码引用的普通标量表现,不能代表过滤是否通过。这和 read_records 返回数组引用的理由相同:接口先固定“返回一个东西”,再由调用者显式解引用或调用。
因此课程中有四种返回边界:解析函数返回记录哈希引用或数组引用,格式化函数返回字符串,过滤器工厂返回代码引用,缺结构查询返回 undef 但可能已经改了容器。course-semantics.pl parse_interface 明确打印 scalar_ref=HASH list_count=1 list_ref=HASH:parse_line 返回的是一个哈希引用,即使调用者写成列表接收,也只是得到一个元素,而不是字段列表。官方 perlsub 对 return 的说明覆盖普通表达式;代码引用的调用语法来自 perlref 的 Code References。测试中的 closure accepts matching record 与 closure rejects short duration 断言的是代码引用被调用后的结果,不是工厂函数本身的真假值。
练习:如果把 read_records 改成 return @records,CLI 的 my $records = read_records($path) 之后执行 @$records 会怎样?返回值会在标量位置变成条数,而不是数组引用,解引用将失败;修改接口必须连同所有调用者和测试一起修改。参考 5.38.2 官方 pod/perlsub.pod(返回与上下文)、pod/perldata.pod(Context、List value constructors),examples/perl-language/evidence/05-08.md。









