我正在从Perl5学习Perl6。
我正在查看副词:exists
https://docs.perl6.org/type/Hash#:exists,但没有:defined
副词
但是我很担心,因为perl5的exists
和defined
:What's the difference between exists and defined?
如何在Perl6中执行类似的操作?
if (defined $hash{key}) {
$hash{key}++;
} else {
$hash{key} = 1;
}
答案 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中,exists
和defined
是关键字。在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的设计目标之一是尽可能减少特殊情况。
这就是exists
或defined
都不是关键字的原因。
如果您查找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
没有{ }
变体的原因是不需要一个。只需询问结果值是否已定义。