Perl:匿名数组嵌套的解引用相关问题

张开发
2026/4/3 14:34:21 15 分钟阅读
Perl:匿名数组嵌套的解引用相关问题
相关文章Perl专栏https://blog.csdn.net/weixin_45791458/category_12190090.html在学习Perl引用时匿名数组和多层嵌套结构往往是比较容易让人困惑的一部分因为初看语法时会发现同一个嵌套数组中的元素居然可以通过多种不同的写法访问出来而且这些写法有的使用了${}有的使用了-有的甚至连-都省掉了这就很容易让人产生一种感觉Perl的解引用规则似乎很乱但实际上只要把引用和解引用的本质想清楚就会发现这些写法并不是毫无规律地并存而只是同一套解引用逻辑在不同语法形式下的表现而已。我们先从最基础的数组引用说起。在Perl中数组引用本质上是一个标量这个标量里存放的是某个数组的地址因此所谓“解引用”本质上就是告诉Perl我现在不是要把这个标量本身当作普通标量来处理而是要把它当作一个指向数组的引用再继续取这个数组里的内容。对于数组引用最常见的解引用方式大致可以分成两类一类是前缀解引用的形式例如{$aref}和 ${$aref}[0]另一类是箭头形式例如$aref-[0]这两类写法在本质上并没有冲突只是表达方式不同而已。先看一个简单例子use strict; use warnings; my array (a, b, c); my $aref \array; {$aref} (A, B, C); ${$aref}[0] a; print array\n;这段代码里$aref是数组array的引用{$aref}表示将$aref这个数组引用解开并把它当成整个数组来使用因此它可以出现在赋值号左边对整个数组重新赋值而${$aref}[0]则表示先把$aref所指向的数组取出来再访问这个数组的第0个元素所以最后打印出来的结果是a B C。这里最重要的一点并不是去死记某种固定写法而是要意识到{...}的意思是“把引用还原成整个数组”${...}[0]的意思是“把引用解开以后继续取下标为0的元素”只要这个思路清楚了类似的写法都不会太难理解。另一种方法是使用-加括号包围的索引值括号对于数组使用[]对于哈希使用{}。解引用这种方式只能对数组或哈希散列的引用使用最后也只能得到原数组成员对散列来说指的是指定键对应的值。如下面的例子所示对数组array进行了引用和解引用操作。再看箭头形式use strict; use warnings; my array (a, b, c); my $aref \array; $aref-[0] A; print array\n;这里$aref-[0]的意思非常直接就是$aref是一个数组引用而-[0]表示取它指向数组的第0个元素所以最后数组内容变成了A b c。在实际代码里箭头写法通常比${$aref}[0]更常见因为它更简洁也更符合阅读习惯尤其是在处理多层嵌套结构时箭头写法的可读性通常会明显更好。有了这些基础之后再来看匿名数组嵌套的情况就容易多了。假设有下面这样一个数据结构use strict; use warnings; my $line [ [1, 2, 3], [4, 5, 6] ]; print ${$line-[0]}[0], \n; # 外层这对 {} 不能省略因为这里解引用的对象是 $line-[0] 这个表达式不是简单标量变量 print ${$line}[0]-[0], \n; # 这里包住 $line 的这对 {} 可以省略可写成 $$line[0]-[0] print $line-[0]-[0], \n; # 这里没有 {}并且第二个 - 可以省略所以也可写成 $line-[0][0] print ${${$line}[0]}[0], \n; # 外层这对 {} 不能省略内层包住 $line 的这对 {} 可以省略 print $line-[0][0], \n; # 这是上一种箭头写法的简化形式连续索引时后面的 - 可以省略 print {$line-[0]}, \n; # 这对 {} 不能省略因为 需要明确作用在 $line-[0] 这个表达式上 print {${$line}[0]}, \n; # 外层这对 {} 不能省略内层包住 $line 的这对 {} 可以省略输出如下1 1 1 1 1 123 123这个例子里$line并不是普通数组而是一个指向匿名数组的引用而这个匿名数组里的两个元素又分别是另外两个匿名数组的引用。所以$line实际上指向的是一个“外层数组”它的第0个元素指向[1,2,3]第1个元素指向[4,5,6]。因此只要某种写法表达的逻辑都是“先从外层数组里取出第0个内层数组引用再从这个内层数组里取出第0个元素”结果都会是1。比如${$line-[0]}[0]这句虽然看起来绕一点但意思并不复杂。因为$line-[0]已经先从$line指向的外层数组中取出了第0个元素而这个元素本身就是一个指向[1,2,3]的数组引用接着${...}[0]表示再对这个数组引用做一次前缀解引用并取其中的第0个元素所以最后得到的就是1。${$line}[0]-[0]则是另一种等价写法它先通过${$line}[0]把$line所指向的外层数组解出来并取第0个元素这一步得到的仍然是内层数组引用然后再通过-[0]访问这个内层数组里的第0个元素因此结果同样是1。$line-[0]-[0]则是最直观的一种写法因为它完整地体现了“数组引用取第0个元素得到另一个数组引用再继续取第0个元素”的逐层访问过程所以通常也是最推荐的写法。${${$line}[0]}[0]本质上和前面几种没有区别只是把解引用过程写得更展开一些它先通过${$line}[0]得到内层数组引用再用${...}[0]取这个内层数组的第一个元素因此结果依旧是1。至于$line-[0][0]其实就是$line-[0]-[0]的简写形式因为Perl里有一个常见规则当多个数组下标或哈希键访问连续出现时中间的-通常可以省略所以$line-[0][0]和$line-[0]-[0]在语义上完全一致。这些写法之所以都能输出1并不是因为Perl允许“随便写”而是因为它们本质上都对应同一个解引用过程只不过有的写得更显式有的写得更简洁。理解了这一点之后再看最后两句输出123的写法也就不难了因为{$line-[0]}和{${$line}[0]}都不是在取单个元素而是在取整个内层数组。$line-[0]和${$line}[0]都会得到那个指向[1,2,3]的数组引用而{...}表示把这个数组引用整体解开所以得到的是整个数组(1,2,3)。之所以打印出来是123而不是1 2 3只是因为print直接输出列表时不会自动插入分隔符而已。要是写成print join(,,{$line-[0]}),\n;结果就会更直观地显示成1,2,3。说到底Perl里的多层嵌套解引用始终都是逐层进行的。不管写法看起来多么不同都必须先拿到下一层引用才能继续访问下一层内容。所以所谓“多种等价写法”并不是说这些写法各自有一套独立规则而是因为Perl同时提供了前缀解引用和箭头解引用两种表达方式并且允许它们在很多场景下混合使用于是同一条访问路径就会呈现出多种不同的写法。不过真正写代码时比“这些写法是否等价”更值得关注的其实是大括号和箭头在什么情况下可以省略、什么情况下不能省略因为这往往才是初学者最容易混淆的地方。一般来说当你用箭头形式访问数组元素或哈希键值并且箭头左边本身就是一个简单的标量引用变量时通常不需要额外加大括号比如$aref-[0]、$href-{key}这种写法就很自然但如果你使用的是前缀解引用形式比如{...}、${...}[0]、%{...}这一类写法大括号往往不能随便去掉因为Perl需要依靠这些大括号来明确解引用操作到底作用在哪个引用表达式上尤其当表达式本身稍微复杂一点时大括号实际上是在帮助语法消歧。还有一个很实用的规则是当多个数组或哈希访问连续出现时第二层以及后面的-通常都可以省略。这就是为什么$line-[0]-[0]可以写成$line-[0][0]而像$data-{users}[0]{name}这样的写法在Perl里也很常见。这个规则让嵌套结构的访问更简洁但前提是访问路径本身已经足够清楚。所以这并不意味着所有箭头都能随便删掉而是说在连续的下标或键访问之间Perl允许把某些显而易见的-省略掉。从代码可读性的角度来看虽然Perl语法允许写出很多复杂而且彼此等价的解引用形式但在实际开发里并不建议为了展示“语法很灵活”而故意把同一个表达式写得过于花哨。因为一旦嵌套层数增加${}、-、{}、%{}这些写法混在一起很容易让代码迅速变得难读。通常来说访问单个元素时优先写成$line-[0][0]这种箭头风格而需要取整个数组时则优先写成{$line-[0]}。这样的写法既清晰也更符合大多数Perl程序员的习惯后期维护起来也更轻松。所以这些不同写法的意义并不是鼓励你在实际代码里把同一个表达式硬写出五六种形式而是为了说明Perl里引用和解引用的底层规则引用始终存放在标量中解引用时既可以用前缀形式也可以用箭头形式多层嵌套时本质上就是逐层展开而连续索引之间的-虽然经常可以省略但并不是所有大括号都可以省略。只要把这套逻辑真正理顺了以后再遇到数组嵌套数组、哈希嵌套数组、数组嵌套哈希之类更复杂的结构时也不过是在重复使用同一套规则而已。最后这个问题其实可以压缩成一句最实用的话在Perl里取单个元素时优先写成$ref-[... ]或更深层的$ref-[...][...]取整个数组时优先写成{$ref}而在嵌套结构里与其追求“有没有更短、更花的写法”不如优先保证写法统一、层次清楚。因为对于引用这种本身就容易绕的语法来说可读性往往比技巧更重要。

更多文章