我的Perl愿望清单:不变符号(第一部分)
测验!问题:这一行中我的错误是什么?
is %HASH{answer}, 'forty-two', '%HASH properly filled';
答案:我找到了正确的答案,但是在HASH
的符号上出了问题。它应该是
is $HASH{answer}, 'forty-two', '%HASH properly filled';
# ^ $, not %
不幸的是,在Perl v5.20+上,这两个语句以相同的方式工作!我直到发布这段代码后才发现问题,cpantesters向我展示了我的错误。这是一个简单的修复,但它提醒我,Perl的变体符号可能会让任何级别的程序员出错。如果我能改变Perl 5的一个地方,我会选择不变符号。
当前情况
在Perl中,符号告诉你期待多少个东西。例如,标量$foo
是单个值。数组@foo
或哈希%foo
中的任何单个值(因为它只是一件东西),也使用$
,所以$foo
、@foo
和%foo
都可以指同一变量的不同部分——或者不同的变量。这种“变体符号”的技术是可行的,但它会混淆新的Perl用户,也让我自己出了错。要知道你在数组或哈希中访问的是什么,你必须查看符号和括号。作为提醒
符号 | 没有括号 | [ ] (数组访问) |
{ } (哈希访问) |
---|---|---|---|
$ |
$z :标量,即单个值 |
$z[0] :数组@z 的第一个元素 |
$z{0} :哈希%z 在键"0" 处的值 |
@ |
@z :数组,即值列表 |
@z[0, 1] :从@z 中获取的两个元素列表($z[0], $z[1]) (数组切片) |
@z{0, "foo"} :从哈希%z 中获取的两个元素列表($z{0}, $z{foo}) |
% |
%z :哈希,即键/值对列表 |
%z[0, 1] :从数组@z 中获取的键和两个值列表(0, $z[0], 1, $z[1]) (哈希切片) |
%z{0, "foo"} :从哈希%z 中获取的键和值列表("0", $z{0}, "foo", $z{foo}) |
使符号成为名称的一部分
为了防止自己重复犯错误,我希望符号成为变量名称的一部分。这不是一个新想法;在Perl、bash和Raku(前身为Perl 6)中,标量就是这样工作的。(见此处)这样,上面的表格看起来会像这样
符号 | 没有括号 | [ ] (数组访问) |
{ } (哈希访问) |
---|---|---|---|
$ |
$z :标量,即单个值 |
$z[0] :不适用 |
$z{0} :不适用 |
@ |
@z :数组,即值列表 |
@z[0] :@z 的第一个元素 |
@z{0} :不适用 |
% |
%z :哈希,即键/值对列表 |
%z[0] :不适用 |
%z{0} :哈希%z 在键0 处的值 |
更简单!对@z
的任何引用都会对名为@z
的数组进行某种操作。
但是切片怎么办?
例如,@z[0,1]
和%z{qw(hello there)}
等切片从数组或哈希中返回多个值。如果符号@
和%
不再可用于切片,我们需要一个替代方案。Perl家族目前提供了两种模型:后缀解引用(“postderef”)语法和后缀副词。
Perl v5.20+支持postderef,这为我们提供了一个选项。Postderef将名称与切片分开
# Valid Perl v5.20+
$hashref->{a}; # Scalar, element at index "a" of the hash pointed to by $hashref
$hashref->@{a}; # List including the "a" element of the hash pointed to by $hashref
$hashref->%{a}; # List including the key "a" and the "a" element of the hash pointed to by $hashref
切片类型在引用之后,而不是在引用之前的符号之前。对于非引用,这个想法会给我们切片语法,如@array@[1,2,3]
或%hash%{a}
。
Raku 提供了另一种选择:“副词”例如 :kv
。例如
# Valid Raku
%hash{"a"} # Single value, element at index "a" of %hash
%hash{"a"}:v; # The same --- just the value
%hash{"a"}:kv; # The list including key "a" and the value of the "a" element of %hash
副词(例如,:kv
)位于后缀位置,紧接在括号或花括号之后。按照这个模式,切片看起来像 @array[1,2,3]:l
或 %hash{a}:kv
。(为了清晰起见,我建议使用 :l
,即 list,而不是 Raku 的 :v
。Raku 的 :v
可以返回标量或列表。)
所以,我看到的选项是(受后缀引用启发 / 受 Raku 启发)
您想要什么 | 没有索引 | [ ] 访问 |
{ } 访问 |
---|---|---|---|
标量 | $z :标量,即单个值 |
@z[0] :从数组中获取单个值 |
%z{0} :在散列 %z 中键 "0" 处的值 |
值列表 | @z :一个数组,即值列表 |
@z@[0, 1] / @z[0, 1]:l :当前写入的列表 ($z[0], $z[1]) |
%z@{0, "foo"} / %z{0, "foo"}:l :当前写入的列表 ($z{0}, $z{"foo"}) |
键/值对列表 | %z :一个散列,即键/值对列表 |
@z%[0, 1] / @z[0, 1]:kv :当前写入的列表 (0, $z[0], 1, $z[1]) |
%z%{0, "foo"} / %z{0, "foo"}:kv :当前写入的列表 ("0", $z{0}, "foo", $z{"foo"}) |
您不一定总能得到您想要的
我更喜欢副词语法。它易于阅读,并且借鉴了 Raku 设计中的所有专业知识。然而,我的偏好必须是可以实现的。我不太确信它不需要重大手术。
Perl 解析器根据切片提供的上下文决定如何解释括号内的内容。解析器将 ...
在 @foo[...]
中的 ...
解释为列表(ref)。在 $foo[...]
中,解析器将 ...
视为标量表达式(ref)。对于任何切片语法,Perl 解析器在解析索引表达式时需要知道所需的结果类型。不幸的是,副词形式在解析索引之后才让解析器猜测。
实际上,您可以修改 Perl 解析器以在看到后缀副词之前保存索引。然后解析器可以应用正确的上下文。我为 @arr[expr]:v
编写了一个 概念证明。它不执行任何代码,但它可以解析后缀副词切片而不会崩溃!然而,在编写这段代码时,我遇到了一个惊喜:新语法并没有绑定到 use v5.xx
指令。
实际上,Perl 解析器允许针对任何 Perl 版本编写的代码使用最新语法。以下两个命令行都在 Perl v5.30 上工作
$ perl -Mstrict -Mwarnings -E 'my $z; $z->@* = 10..20'
# ^ -E: use all the latest features
$ perl -Mstrict -Mwarnings -e 'my $z; $z->@* = 10..20' # (!!!)
# ^ -e: not the latest features
第二个命令行没有 use v5.30
,因此您不能使用(在 v5.10 中引入的)say
。但是,您可以使用后缀引用(从 v5.20 开始)!
由于解析器允许旧程序使用新语法,因此任何建议添加到 Perl 语法中的内容在所有以前的 Perl 版本中都必须没有意义。后缀副词未能通过这一测试。例如,以下是一个有效的 Perl 程序
sub kv { "kv" }
my @arr = 10..20;
print 1 ? @arr[1,2]:kv;
# ^^^^^^^^^^^^ valid Perl 5 syntax, but not a slice :(
print "\n";
我首选的切片语法可能会改变现有程序的含义,所以我看起来无法得到我的首选。
下一步
这并不是故事的结局!在第二部分中,我将更深入地探讨 Perl 的解析器和词法分析器。我将分享我在调查后缀引用时发现的惊喜。然后,我将描述通向不变符号的可能路径以及它们可以提供的简单性。
标签
Christopher White
Chris White是一位经验丰富、多产的发明家、公众演讲者、专利代理人、计算机工程师、演示场景制作人和软件开发者。他目前正在为D3 Engineering构建嵌入式Linux系统。他不定期地在博客上撰写关于技术、音乐和奶酪的文章。
浏览他的文章
反馈
这篇文章有什么问题吗?请通过在GitHub上打开一个问题或拉取请求来帮助我们。