Perl6哈希键已定义与存在

时间:2019-02-04 16:49:27

标签: perl6

我正在从Perl5学习Perl6。

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

但是我很担心,因为perl5的existsdefinedWhat's the difference between exists and defined?

之间是有区别的

如何在Perl6中执行类似的操作?

if (defined $hash{key}) {
   $hash{key}++;
} else {
   $hash{key} = 1;
}

3 个答案:

答案 0 :(得分:6)

if defined %hash{'key'} {
   %hash{'key'}++;
} else {
   %hash{'key'} = 1;
}

使用defined例程或方法。参见5to6-perlfunc -- defined

答案 1 :(得分:3)

由于具有自动生存能力,因此在Perl 6中可以轻松地获得确切的代码:

my %foo;
%foo<thekey>++;
say %foo.perl; # => {:thekey(1)}

除此之外,defined %foo<thekey>%foo<thekey>:exists之间的区别在于,您可以在给定键的哈希值中放入未定义的值,它将对{{1 }},但从True:exists

答案 2 :(得分:2)

defined是值的属性。

exists是哈希索引操作的变体选择器。

在Perl5和Perl6中都是如此。
只是他们处理问题的方式不同。


假设您有这样的哈希值:

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

它具有三个键:

say %h.keys; # (a b c)

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

my $value = %h{'a'};

您可以询问该值是否为defined

say defined $value; # True

现在让我们尝试使用c

my $value = %h{'c'};
say defined $value; # False

使用D

my $value = %h{'D'};
say defined $value; # False

请注意,即使D不是哈希中的键,您获得的值也与查找c相同。

该值不知道您如何获得它。它不了解哈希索引操作。
即使不将其存储在变量中也是如此。

say defined %h{'c'}; # False
say defined %h{'D'}; # False

因此,我们必须告诉索引操作以提供该信息。

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

另外,如果将%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

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

在Perl5中,existsdefined是关键字。在Perl6中不是。
在Perl6中,它们在形式,形式上都不特殊。

以下是用于检查exists的Perl5版本:

use v5.12.0;
use warnings;

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

my $exists = exists $h{D};
say $exists ? 'True' : 'False'; # False

如果您使用-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
…

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

defined的列表非常相似。


Perl6的设计目标之一是尽可能减少特殊情况。 这就是existsdefined都不是关键字的原因。

如果您查找defined在Rakudo代码库中的位置,则会发现:

proto sub defined(Mu, *%) is pure {*}
multi sub defined(Mu \x) { x.defined }

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

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

所有值继承的默认值位于Mu.defined source中:

proto method defined(|) {*}
multi method defined(Mu:U: --> False) { }
multi method defined(Mu:D: --> True)  { }

因此默认情况下,类型对象是未定义的,实例是要定义的。

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

multi method defined(Failure:D: --> False) { $!handled = 1 }

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


:exists上的%h{'D'}又如何呢?
在Perl6中,“ normal ”运算符被定义为具有特殊名称的子例程。

sub foo (%hash, $key){}

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

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

请注意,后缀运算符{ }由多个多子项支持,但我们实际上只需要查看one of them

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

您可以忽略大部分。要注意的关键是:$exists!
默认情况下,命名参数是可选的,并且不会说出选择了multi的说法。 为了使:exists强制选中该选项,必须将其标记为必需的!

每当看到:exists时,您都可以认为它是:exists(True)exists => True的缩写。
(而且:$exists也是:exists($exists)exists => $exists的缩写。)

因此,这两行在功能上是相同的:

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

my $exists = postcircumfix:<{ }> %h, 'D', exists => True;

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

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

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

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

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


因此后缀:defined没有{ }变体的原因是不需要一个。只需询问结果值是否已定义。