Perl 标量上下文中如何得到列表

本文说明了为什么在某些情况下,标量上下文中使用”列表“无法得到本身元素的个数,以及如何获得你所期望的结果。


1. “列表”在标量上下文中并非一定会得到元素的个数

或许你觉得这个标题有一些奇怪:按照Perl 上下文的定义,列表在标量上下文中会得到列表元素的个数。单纯通过这个逻辑来看,下面的语句似乎会让$scalar 被赋值3:

$scalar = qw/a b c/;
然而Perl 总会在一些不起眼的地方强行给你一个大惊喜,如果你打印$scalar 的值,你会发现你得到的是"c"而非"3"。


2. 为什么可以被称为真理的上下文规则“失效”了

似乎对于非Perl 程序员,说明这个问题要简单许多。为了理解为什么上下文规则在这里“失效”了,我们必须抛开“列表在标量上下文中会得到列表元素的个数”这样一个看似是真理的结论,而从语言本身再去看待这个问题。

仍然是这条语句

$scalar = qw/a b c/;
Perl 中所有的qw 运算符都会被展开成列表形式,所以这条语句和下一条完全等价:

$scalar = ('a', 'b', 'c',);
当然这两条语句都会把$scalar 赋值为"c"。

下面让我们忘掉上下文规则,通过Perl 的眼睛去看看在这里Perl 看到了什么:

1. 赋值号之后是一个左括号,而括号是一种优先级约束,括号中的表达式被优先计算。

2. 括号中是由一串被逗号隔开的表达式,所以理所当然的是一个逗号表达式。

3. 逗号表达式的值是最后一个表达式的值,最后一个表达式是'c',它的值也是'c'。

4. 所以整个逗号表达式的值是'c'

5. 将'c' 赋值给$scalar。

这就是Perl 的眼睛所看到的东西,也是为什么$scalar 会被赋值为'c' 的原因。

所以给你一条语句:

$scalar = scalar qw/a b c/;
如果你开始以Perl 的眼睛来看代码,你就会明白上面的语句和下一条语句等价:

$scalar = scalar 'c';
理所当然,你得到的仍然是"c" 而非"3"。


3. 我们所熟知的Perl 真理难道是谬论吗?

对于Perl 程序员有一个信条:上下文规则即是绝对的真理。因为这条规则就是Perl 的思考方式,正确性不容置疑。

当然,这条真理在上面的语句中仍然有效。

之所以会带来上下文规则失效了的错觉,是因为我们往往习惯以自己的眼睛而非Perl 的眼睛去看代码。刚才在解释$scalar 为什么会得到'c' 的时候,我们也间接地表达了一个事实,在语句

$scalar = qw/a b c/;
$scalar = ('a', 'b', 'c',);
$scalar = scalar qw/a b c/;
甚至是语句
$scalar = scalar ('a', 'b', 'c',);
中,都不存在任何列表

上下文规则不可能出错,列表在标量上下文中返回的永远都是列表中元素的个数,然而在我们前面写的所有语句中,的确不曾出现过任何一个列表。


4. 如何在标量上下文中使用列表

如果想这样做,方法就是显式的告诉Perl 赋值号右边是一个列表而非逗号表达式:

$scalar = @{ ['a', 'b', 'c',] };
其中赋值号右边的方括号告诉Perl 这是一个匿名列表的引用,而@{$aref} 则是列表的解引用形式,所以赋值号右边我们得到了一个列表"('a', 'b', 'c')", 而非逗号表达式"('a', 'b', 'c')"。而根据上下文规则,列表在标量上下文中会得到列表元素的个数。理所当然$scalar 被赋值为3。

回过头来,让我们看另一条语句:

@array = qw/a b c/;
根据qw 展开规则,这条语句和下面一条语句等价:

@array = ('a', 'b', 'c',);

赋值号左边是一个列表,所以这条语句是一个列表上下文,Perl 会在列表上下文中将赋值号的右边理解为一个列表"('a', 'b', 'c',)" 而非逗号表达式"('a', 'b', 'c',)"。这个不难理解——在列表上下文中理所当然Perl 会期望一个列表,而在标量上下文中理所当然Perl 会期望一个标量。所以相同的表达式

('a', 'b', 'c',)
会在标量上下文中被当作一个逗号表达式,而在列表上下文中被认为是一个列表。Perl 只是在寻找它期望得到的东西。


5. 去理解Perl 而非让Perl 理解你

这件事是2 年前自己学Perl 的时候也非常不解的一件事,在查阅了不少资料后才理解为什么我的代码会这样运作。我觉得这也是一个Perl 程序员所必须经历的一个转变:

Perl 和其他语言有些不同——写Perl 的时候需要用Perl 的思维去思考,你需要理解Perl,而非让Perl 去理解你的代码。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值