Perl 07:给 $_[0] 赋值可能改掉调用者
数据规范化需要把一个服务名转成另一种写法,但把“返回规范值”误写成“修改第一个参数”,调用者原先的记录就被污染了。在没有 signatures 的传统子程序里,@_ 的元素可能是调用参数的别名;这不等同于一个完全独立的形参数组。
examples/perl-language/experiments/07-arguments.pl 用一个可写的变量调用 mutate_argument($service),子程序写 $_[0] = 'worker'。实测 STDOUT 第一行 after-alias=worker;第二个函数先用 my ($service) = @_ 接收值,再把局部变量改成 cache,返回 local=cache,外面的值仍是 after-copy=worker。该演示只展示了可写标量变量作为实参的情况;如果传不可写常量,修改可能直接报错。局部复制也不是深拷贝:实参若是可变对象的引用,复制后的引用仍能改动同一目标。
因此日志工具把调用契约写到数据结构边界:format_record($record) 接收记录引用,只读取固定字段;read_records($path) 返回记录数组引用;调用方不能把它们当作“参数永远由 Perl 自动隔离”的保证。未来规范化函数会声明是产生新记录还是允许就地修改,避免两个约定混在一个接口里。
补充实验把 @_ 的传统别名与 E01 的 signatures 对照起来。mutate_argument($service) 说明 $_[0] 可以写回调用者;course-semantics.pl closure 里 make_filter(%conditions) 则先把条件复制到词法变量,再返回闭包。前者演示别名风险,后者演示如何在接口边界切断调用者后续修改。两者都不依赖隐藏状态:一个是 perlsub 规定的 @_ 元素别名,一个是普通词法变量被闭包捕获。
E01 的签名参数绑定到新的词法变量,能避免直接改 $_[0] 这一类错误,但签名并不会深拷贝引用,也不会替业务校验等级值。课程测试同时保留 @_、signatures 和闭包三个入口:传统子例程要主动复制需要隔离的值;签名负责参数个数和绑定形状;闭包负责保存已经校验过的条件。
练习:mutate_argument('api') 为什么不能照搬变量例子的后置断言?字面量没有一个供调用者观察的可写 $service;按本机实际语义可能报“read-only value”,不应该拿它证明 @_ 从不别名。正反对照输出及状态在 examples/perl-language/evidence/05-08.md;参考经校验的 Perl 5.38.2 pod/perlsub.pod(DESCRIPTION 的 @_ alias 与 my())。









