为什么在Perl中访问数组和哈希元素时需要$?
由于数组和哈希只能在Perl中包含标量,因此为什么在访问数组或者哈希元素时必须使用$来告诉解释器该值是标量?换句话说,假设我们有一个数组@myarray和一个哈希%myhash,为什么需要这样做:
$x = $myarray[1]; $y = $myhash{'foo'};
而不是仅仅做:
$x = myarray[1]; $y = myhash{'foo'};
为何上述模棱两可?
如果在那个地方不是$,那不是非法的Perl代码吗?例如,以下所有内容在Perl中不是非法的吗?
@var[0]; @var{'key'}; %var[0]; %var{'key'};
解决方案
我可以想到一种方式
$x = myarray[1];
如果我们想要一个名为m的数组,该如何确定?
$x = m[1];
除了正则表达式匹配之外,我们如何分辨呢?
换句话说,语法可以帮助Perl解释器解释!
切片不是非法的:
@slice = @myarray[1, 2, 5]; @slice = @myhash{qw/foo bar baz/};
而且我怀疑这是我们需要指定是否要从哈希/数组中获取单个值的部分原因。
这是有效的Perl:@var [0]
。它是长度为一的数组切片。 @var [0,1]将是长度为2的数组切片。
@var ['key']是无效的Perl,因为只能用数字索引数组,并且 其他两个(
%var [0]和%var ['key']`)无效,因为散列片使用{}来索引散列。
不过," @ var {'key'}"和" @var {0}"都是有效的哈希片。显然,获取长度为1的切片是不正常的,但肯定是有效的。
有关在Perl中进行切片的更多信息,请参见perldata perldoc的slice部分。
印记会为我们提供容器的退货类型。因此,如果某事以@
开头,那么我们会知道它会返回一个列表。如果以$
开头,则返回标量。
现在,如果标记后面只有一个标识符(如$ foo或者@foo),则它是一个简单的变量访问。如果后跟一个[[,则它是对数组的访问,如果它后面是一个" {",它是对散列的访问。
# variables $foo @foo # accesses $stuff{blubb} # accesses %stuff, returns a scalar @stuff{@list} # accesses %stuff, returns an array $stuff[blubb] # accesses @stuff, returns a scalar # (and calls the blubb() function) @stuff[blubb] # accesses @stuff, returns an array
一些人类语言具有非常相似的概念。
但是,许多程序员发现这令人困惑,因此Perl 6使用了不变标记。
通常,Perl 5编译器希望在编译时知道列表中或者标量上下文中是否包含某些内容,因此,如果没有前导符号,某些术语将变得模棱两可。
标记提供了访问的上下文:
- $表示标量上下文(标量变量或者哈希或者数组的单个元素)
- " @"表示列表上下文(整个数组或者哈希的一部分或者数组)
- "%"是整个哈希
我刚用过
my $x = myarray[1];
在一个程序中,令我惊讶的是,当我运行它时发生了什么:
$ perl foo.pl Flying Butt Monkeys!
那是因为整个程序看起来像这样:
$ cat foo.pl #!/usr/bin/env perl use strict; use warnings; sub myarray { print "Flying Butt Monkeys!\n"; } my $x = myarray[1];
因此,myarray调用了一个子例程,将其传递给对包含单个元素1的匿名数组的引用。
这是在阵列访问上需要标记的另一个原因。
在Perl 5(将在Perl 6中进行更改)中,符号表示表达的上下文。
- 我们希望散列中没有特定的标量,因此它是
$ hash {key}
。 - 我们想要数组中某个特定插槽的值,因此它是$ array [0]。
但是,正如zigdon所指出的那样,切片是合法的。他们在列表上下文中解释这些表达式。
- 我们想要在哈希
@hash {key}
工作中包含1个值的列表 - 但是也可以使用较大的列表,例如`@hash {qw <key1 key2 ... key_n>}。
- 你想在数组中使用几个插槽
@array [0,3,5..7,$ n .. $ n + 5]
工作 - @array [0]是大小为1的列表。
没有"哈希上下文",因此%hash {@keys}和%hash {key}都没有意义。
因此,我们具有" @"
+" array [0]"
<=> <sigil = context> + <索引表达式>作为完整表达式。
人们已经指出,我们可以有切片和上下文,但是这里有sigils可以将变量内容与其他所有内容分开。我们不必知道所有关键字或者子例程名称即可选择明智的变量名称。对于其他语言的Perl,这是我最想念的事情之一。
在Perl 5中,我们需要标记($和@),因为裸字标识符的默认解释是子例程调用的解释(因此,在大多数情况下无需使用&)。