使用Perl6散列键定义与存在

con*_*con 5 perl6

我正在从Perl5学习Perl6.

我正在查看副词:exists https://docs.perl6.org/type/Hash#:exists但是没有:defined副词

但我很担心,因为perl5 exists与&之间有区别defined:存在和定义之间有什么区别?

我怎样才能在Perl6中做这样的事情?

if (defined $hash{key}) {
   $hash{key}++;
} else {
   $hash{key} = 1;
}
Run Code Online (Sandbox Code Playgroud)

uge*_*exe 6

if defined %hash{'key'} {
   %hash{'key'}++;
} else {
   %hash{'key'} = 1;
}
Run Code Online (Sandbox Code Playgroud)

使用defined例程或方法.见5to6-perlfunc - 定义


Bra*_*ert 6

defined 是一个值的属性。

exists 是散列索引操作的变体选择器。

这在 Perl5 和 Perl6 中都是正确的。
只是他们处理的方式不同而已。


假设你有一个这样的哈希:

my %h = ( a => 1, b => 2, c => Int );
Run Code Online (Sandbox Code Playgroud)

它有三个键:

say %h.keys; # (a b c)
Run Code Online (Sandbox Code Playgroud)

您可以获取与键关联的值:

my $value = %h{'a'};
Run Code Online (Sandbox Code Playgroud)

你可以询问价值是否是 defined

say defined $value; # True
Run Code Online (Sandbox Code Playgroud)

现在让我们尝试一下c

my $value = %h{'c'};
say defined $value; # False
Run Code Online (Sandbox Code Playgroud)

并与D

my $value = %h{'D'};
say defined $value; # False
Run Code Online (Sandbox Code Playgroud)

请注意,即使D不是散列中的键,您返回的值看起来与查找相同c

价值不知道你是如何得到它的。它对哈希索引操作一无所知。
即使您不将其存储在变量中,它也是一样的。

say defined %h{'c'}; # False
say defined %h{'D'}; # False
Run Code Online (Sandbox Code Playgroud)

所以我们必须告诉索引操作而不是给我们这些信息。

my $exists = %h{'D'}:exists;
say $exists; # False
Run Code Online (Sandbox Code Playgroud)

此外,如果您将结果%h{'D'}用作容器,它[神奇地] 开始存在。

say %h{'D'}:exists; # False

say %h{'D'}.perl;   # Any
say %h{'D'}:exists; # False

%h{'D'}++;
say %h{'D'}:exists; # True

say %h{'D'}; # 1
Run Code Online (Sandbox Code Playgroud)

那么为什么它与 Perl5 不同呢?

在 Perl5 中existsdefined是关键字。在 Perl6 中它们不是。
它们在 Perl6 中没有任何特别之处、形状或形式。

这是用于检查的 Perl5 版本exists

use v5.12.0;
use warnings;

my %h = ( a => 1, b => 2, c => undef );

my $exists = exists $h{D};
say $exists ? 'True' : 'False'; # False
Run Code Online (Sandbox Code Playgroud)

如果您调用编译器,-MO=Concise您将获得它将执行的操作码列表。

这只是显示这exists是一个操作码的部分:

…
d     <;> nextstate(main 5 -e:1) v:%,us,*,&,{,$,fea=2 ->e

g     <2> sassign vKS/2 ->h                          # <----- =

-        <1> ex-exists sK/1 ->f                      # <-\
e           <+> multideref($h{"D"}) sK/EXISTS ->f    # <--+-- exists $h{D}
-              <0> ex-padhv sR ->e                   # <-/

f        <0> padsv[$exists:5,6] sRM*/LVINTRO ->g     # <----- $exists

h     <;> nextstate(main 6 -e:1) v:%,us,*,&,{,$,fea=2 ->i
…
Run Code Online (Sandbox Code Playgroud)

multideref($h{"D"})标有EXISTS,且有ex-exists操作码。

的列表defined非常相似。


Perl6 的设计目标之一是尽可能减少特殊情况。这就是为什么既不exists或者defined是关键字。

如果您查找definedRakudo 代码库中的位置,您会发现:

proto sub defined(Mu, *%) is pure {*}
multi sub defined(Mu \x) { x.defined }
Run Code Online (Sandbox Code Playgroud)

defined只是一个接受一个值的子例程,并调用该.defined值的方法。

由于它执行方法调用,因此该值可以决定是否认为已定义。

所有值继承的默认值都在Mu.defined source 中

proto method defined(|) {*}
multi method defined(Mu:U: --> False) { }
multi method defined(Mu:D: --> True)  { }
Run Code Online (Sandbox Code Playgroud)

所以默认是类型对象未定义,实例被定义。

一个值得注意的例外是Failure对象:

multi method defined(Failure:D: --> False) { $!handled = 1 }
Run Code Online (Sandbox Code Playgroud)

因此,这使得 Failure 对象的实例被视为未定义。它也将导致故障认为自己已处理。
(如果没有处理失败,它会在垃圾收集时发出警告。)


那么:exists%h{'D'}呢?
在 Perl6 中,“普通”运算符被定义为具有特殊名称的子程序。

sub foo (%hash, $key){}

say &foo.signature.perl;
# :(%hash, $key)

say &postcircumfix:<{ }>.signature.perl;
# :($, $?, Mu $?, *%)
Run Code Online (Sandbox Code Playgroud)

请注意,postcircumfix 运算符{ }由多个多子支持,但我们实际上只需要查看其中一个

multi sub postcircumfix:<{ }>( \SELF, \key, Bool() :$exists!, *%other ) is raw {
    SLICE_ONE_HASH( SELF, key, 'exists', $exists, %other )
}
Run Code Online (Sandbox Code Playgroud)

你可以忽略大部分。要注意的关键是:$exists!
默认情况下,命名参数是可选的,并且没有选择多的说法。为了:exists强制选择该选项,必须将其标记为“required” !

每当您看到时,:exists您都可以将其视为:exists(True)and 的缩写exists => True
(也是and 的:$exists缩写。):exists($exists)exists => $exists

所以这两行在功能上是相同的:

my $exists = %h{'D'}:exists;

my $exists = postcircumfix:<{ }> %h, 'D', exists => True;
Run Code Online (Sandbox Code Playgroud)

它告诉操作员(子程序)使用exists变体。

请注意,它还传递了 的值$exists,这意味着它可以进行反向存在检查。

say %h{'c'}:exists;  # True
say %h{'c'}:!exists; # False

say %h{'D'}:exists;  # False
say %h{'D'}:!exists; # True
Run Code Online (Sandbox Code Playgroud)

:!exists:exists(False)and 的缩写exists => False


所以没有:definedpostcircumfix 变化的原因{ }是不需要有一个。只需询问结果值是否已定义。