class Hash is Map { }

Hash 是可变的 Map;它通过继承 Map 来实现 Associative,因此支持使用键查找值,支持 关联下标

Hash 是带有 % 符号的变量的默认类型。

哈希是键到值的映射,在其他编程语言中称为 dict(Python)、object(Javascript)或 Hash Map(Java)。

基本用法

# initialization with pairs: 
my %capitals = Spain => 'Madrid''United States' => 'Washington DC';
 
# adding another pair: 
%capitals{'Zimbabwe'} = 'Harare';
 
# accessing a value by key: 
my $country = 'Spain';
say "The capital of $country is %capitals{$country}";
 
# getting all keys: 
say "I know the capitals of these countries: "%capitals.keys.sort.join('');
 
# check if a key is in a hash: 
if %capitals{'Europe'}:exists {
    # not executed 
}
 
# iterating over keys and values (unordered): 
for %capitals.kv -> $country$capital {
    say "$capital is the capital of $country";
}

尽管哈希的顺序在每次调用中都是随机的,但连续调用 .keys.values 仍会按相同顺序返回它们

my %orig = :1a, :2b; my %new = :5b, :6c;
%orig{ %new.keys } = %new.values;
say %orig.raku# OUTPUT: «{:a(1), :b(5), :c(6)}␤»

在这种情况下,b 将始终与 5 关联,c 将始终与 6 关联;即使连续两次调用 keys 会以不同的顺序返回它们。在任何程序调用中,对它们中的任何一个单独和重复调用都将始终返回相同的顺序。

请参阅 哈希字面量 部分,了解声明哈希的不同方法。此外,只要遵循这些规则,就可以使用花括号声明它们

  • 空花括号将始终声明一个空哈希。

  • 相反,对 $_ 的引用(即使是隐式的)将声明一个块。

  • Pair 或具有 % 作为第一个元素的变量将声明一个哈希。

given 3 { say WHAT {3 => 4:b}  };     # OUTPUT: «(Hash)␤» 
given 3 { say WHAT {3 => 4:b($_)} };  # OUTPUT: «(Block)␤» 
given 3 { say WHAT {3 => 4:b(.Num)} };# OUTPUT: «(Block)␤» 
say { 'a',:b(3), 'c' }.^name;           # OUTPUT: «Block␤» 

最后两个案例是生成 Block 的示例,其中存在主题变量 $_。最后一个案例不满足生成哈希的第三个条件,因此生成 Block

在括号或方括号前加上 % 将生成 Hash,只要元素可以配对。

say %'a'3:b(3), 'c'3 ).^name# OUTPUT: «Hash␤»

此哈希中的元素可以配对 Pair :b(3) 的两侧。

say %a b c 1 2 3»).^name;           # OUTPUT: «Hash␤»

空哈希可以用空花括号或从 6.d 开始的 %() 初始化。

say %().^name# OUTPUT: «Hash␤» 
say {}.^name;  # OUTPUT: «Hash␤»

哈希可以用类型参数化。您可以这样更改键的类型

my %next-prime{Int} = 2 => 33 => 55 => 77 => 1111 => 13;

值的类型默认为 Mu,但您可以将其限制为其他类型

my Array %lists;

您可以结合这两个功能

my Array %next-primes{Int} = 2 => [35], 11 => [1317];

方法§

classify-list 方法§

multi method classify-list(&mapper*@list:&as --> Hash:D)
multi method classify-list(%mapper*@list:&as --> Hash:D)
multi method classify-list(@mapper*@list:&as --> Hash:D)

使用给定的 mapper 对可能为空的 @list 值进行分类,填充 Hash,并使用 :&as Callable 更改值(可选)。@list 不能是惰性的。

映射器可以是接受单个参数的 Callable,一个 Associative 或一个 Iterable;保证每个项目仅调用一次此 Callable。对于 AssociativeIterable 映射器,@list 中的值分别表示映射器值的键和索引。将对 @list 中的每个项目执行一次 Callable 映射器,其中该项目作为参数,其返回值将用作映射器的值。

简单分类§

在简单分类模式下,每个映射器的值都是任何非 Iterable,表示对 @list 的项目进行分类的键

say % .classify-list: { $_ %% 2 ?? 'even' !! 'odd' }^10;
# OUTPUT: «{even => [0 2 4 6 8], odd => [1 3 5 7 9]}␤» 
 
my @mapper = <zero one two three four five>;
my %hash = foo => 'bar';
say %hash.classify-list: @mapper12344;
# OUTPUT: «{foo => bar, four => [4 4], one => [1], three => [3], two => [2]}␤» 

映射器的值用作将 @list 的项目 pushed 到的 Hash 的键。如果您希望一次将项目分类到多个类别,请参阅 .categorize-list

多级分类§

在多级分类模式下,每个映射器的值是一个 Iterable,表示对 @list 的项目进行分类的哈希键树

say % .classify-list: {
    [
        (.is-prime ?? 'prime' !! 'non-prime'),
        ($_ %% 2   ?? 'even'  !! 'odd'      ),
    ]
}^10;
# OUTPUT: 
# { 
#     non-prime => { 
#         even => [0 4 6 8], 
#         odd  => [1 9] 
#     }, 
#     prime => { 
#         even => [2], 
#         odd  => [3 5 7] 
#     } 
# }

如果我们使用 Iterable 而不是 Callable,则这些 Iterable 中的每一个都必须具有相同数量的元素,否则该方法将引发异常。当同一个键是某个值的分类的叶节点,但却是另一个值的分类的节点时,此限制是为了避免冲突。

my @mapper = [['1a','1b','1c'],['2a','2b','2c'],['3a','3b','3c']];
say % .classify-list: @mapper1,2,1,1,2,0;
# OUTPUT: «{1a => {1b => {1c => [0]}}, 2a => {2b => {2c => [1 1 1]}}, 3a => {3b => {3c => [2 2]}}}␤» 

数组的每个元素表示树中的不同级别,其中正在映射的列表的元素用作索引,映射器数组的元素用作不同级别的键。因此,0 选择第一个子数组,然后通过遍历该子数组的其余元素来构建后续级别。

my @mapper = [['1a','1b'],['2a','2b'],['3a','3b']];
say % .classify-list: @mapper1,0,1,1,1,0,2;
# OUTPUT: «{1a => {1b => [0 0]}, 2a => {2b => [1 1 1 1]}, 3a => {3b => [2]}}␤» 

从 6.d 版本开始,尝试使用不同大小的 Iterable 将引发错误

my @mapper = [<1a 1b>, <2a 2b 2fail>];
say % .classify-list: @mapper1,0,1,1,1,0;
# OUTPUT: «mapper on classify-list computed to an item with different number 
# of elements in it than previous items, which cannot be used because all 
# values need to have the same number of elements. Mixed-level classification 
# is not supported.␤  in block <unit>…» 

:&as 值修改器§

如果指定了 :&as Callable 参数,则它将对 @list 的每个项目调用一次,其中值作为参数,其返回值将用作原始 @list 的项目

say % .classify-list: :as{"Value is $_"}{ $_ %% 2 ?? 'even' !! 'odd' }^5;
# OUTPUT (slightly altered manually, for clarity): 
# { 
#     even => ['Value is 0', 'Value is 2', 'Value is 4'], 
#     odd  => ['Value is 1', 'Value is 3'] 
# }

方法 categorize-list§

multi method categorize-list(&mapper*@list:&as --> Hash:D)
multi method categorize-list(%mapper*@list:&as --> Hash:D)
multi method categorize-list(@mapper*@list:&as --> Hash:D)

使用给定的 mapper 对可能为空的 @list 值进行分类,填充 Hash,并使用 :&as Callable 更改值(可选)。@list 不能是惰性的。

映射器可以是接受单个参数的 Callable,一个 Associative 或一个 Iterable。对于 AssociativeIterable 映射器,@list 中的值分别表示映射器值的键和索引。将对 @list 中的每个项目执行一次 Callable 映射器,其中该项目作为参数,其返回值将用作映射器的值。

简单分类§

映射器的值应为可能为空的非 Iterable 列表,表示将值放入其中的类别

say % .categorize-list: {
    gather {
        take 'prime'   if .is-prime;
        take 'largish' if $_ > 5;
        take $_ %% 2 ?? 'even' !! 'odd';
    }
}^10;
 
# OUTPUT: 
# { 
#     prime   => [2 3 5 7] 
#     even    => [0 2 4 6 8], 
#     odd     => [1 3 5 7 9], 
#     largish => [6 7 8 9], 
# }

请注意某些项,例如 67,出现在多个类别中。

多级分类§

在多级分类中,映射器生成的类别是 可迭代对象,分类结合了 classify 的特性,通过为每个类别生成嵌套的分类哈希。

say % .categorize-list: {
    [
        $_ > 5    ?? 'largish' !! 'smallish',
        .is-prime ?? 'prime'   !! 'non-prime',
    ],
}^10;
 
# OUTPUT: 
# { 
#     largish => { 
#         non-prime => [6 8 9], 
#         prime     => [7] 
#     }, 
#     smallish => { 
#         non-prime => [0 1 4], 
#         prime     => [2 3 5] 
#     } 
# }

上面代码段中的映射器生成一个单项列表(注意尾随逗号),其中包含一个两项 Array。该数组中的第一项表示分类的第一级:该例程生成的 largish/smallish 类别。该数组中的第二项表示进一步的分类级别,在我们的例子中,表示在每个类别内部对 prime/non-prime 的分类。

注意::每个 Iterable 类别必须具有相同数量的元素,否则该方法将抛出异常。此限制的存在是为了避免在同一键作为某个值分类的叶节点但作为另一个值分类的节点时发生冲突。

:&as 值修饰符§

如果指定了 :&as Callable 参数,则它将对 @list 的每个项目调用一次,其中值作为参数,其返回值将用作原始 @list 的项目

say % .categorize-list: :as{"Value is $_"}{ $_ %% 2 ?? 'even' !! 'odd' }^5;
# OUTPUT (slightly altered manually, for clarity): 
# { 
#     even => ['Value is 0', 'Value is 2', 'Value is 4'], 
#     odd  => ['Value is 1', 'Value is 3'] 
# }

方法 push§

method push(Hash:D: +new)

使用与哈希赋值相同的语义将 new 元素添加到哈希中,但有三个例外

  • 哈希不会首先清空,即不会删除旧对。

  • 如果哈希中已存在某个键,并且相应的值是 Array,则将新值推送到数组上(而不是替换它)。

  • 如果哈希中已存在某个键,并且相应的值不是 Array,则旧值和新值都将放入一个数组中,以替换旧值。

示例

my %h  = => 1;
%h.push: (=> 1);              # a => [1,1] 
%h.push: (=> 1xx 3 ;        # a => [1,1,1,1,1] 
%h.push: (=> 3);              # a => [1,1,1,1,1], b => 3 
%h.push('c' => 4);              # a => [1,1,1,1,1], b => 3, c => 4 
push %h'd' => 5;              # a => [1,1,1,1,1], b => 3, c => 4, d => 5

请注意,参数列表中的文字对可能会被解释为 命名参数,因此不会出现在 Hash

my %h .= push(=> 6);
say %h.raku# OUTPUT: «{}␤»

使用相应的 子例程 来捕获此类错误

push my %h=> 7;
CATCH { default { put .message } };
# OUTPUT: «Unexpected named argument 'f' passed␤»

另请注意,push 可以用作哈希初始化期间赋值的替代方法,非常有用。以倒排索引为例

my %wc = 'hash' => 323'pair' => 322'pipe' => 323;
(my %inv).push: %wc.invert;
say %inv;                     # OUTPUT: «{322 => pair, 323 => [pipe hash]}␤»

请注意,这样的初始化也可以写成

my %wc = 'hash' => 323'pair' => 322'pipe' => 323;
my %inv .= push: %wc.invert;

注意::与 append 相比,push 将按原样添加给定值,而 appendslip 添加它

my %ha = :a[42, ]; %ha.push: "a" => <a b c a>;
say %ha# OUTPUT: «{a => [42 (a b c a)]}␤» 
 
my %hb = :a[42, ]; %hb.append: "a" => <a b c a>;
say %hb# OUTPUT: «{a => [42 a b c a]}␤»

方法 append§

method append(+@values)

将提供的对或偶数大小的列表附加到哈希中。如果键已存在,则将现有值转换为 Array,并将新值推送到该 Array 上。请注意,您不能混合偶数大小的列表和对列表。此外,裸 Pair 或冒号对将被视为 .append命名参数

my %h = => 1;
%h.append('b'2'c'3);
%h.append( %(=> 4) );
say %h;
# OUTPUT: «{a => 1, b => 2, c => 3, d => 4}␤» 
%h.append('a'2);
# OUTPUT: «{a => [1 2], b => 2, c => 3, d => 4}␤»

注意::与 push 相比,appendslip 给定值,而 push 将按原样添加它

my %hb = :a[42, ]; %hb.append: "a" => <a b c a>;
say %hb# OUTPUT: «{a => [42 a b c a]}␤» 
 
my %ha = :a[42, ]; %ha.push: "a" => <a b c a>;
say %ha# OUTPUT: «{a => [42 (a b c a)]}␤»

方法 default§

method default(Hash:D:)

返回调用者的默认值,即在使用不存在的键访问 Hash 中的元素时返回的值。除非使用 is default 特性将 Hash 声明为具有默认值,否则该方法将返回类型对象 (Any)

my %h1 = 'apples' => 3'oranges' => 7;
say %h1.default;                                       # OUTPUT: «(Any)␤» 
say %h1{'bananas'};                                    # OUTPUT: «(Any)␤» 
 
my %h2 is default(1= 'apples' => 3'oranges' => 7;
say %h2.default;                                       # OUTPUT: «1␤» 
say %h2{'apples'} + %h2{'bananas'};                    # OUTPUT: «4␤»

method keyof§

method keyof()

返回调用者的键的类型约束。对于普通哈希,该方法返回强制类型 (Str(Any)),而对于 非字符串键 哈希,返回在 Hash 声明中使用的类型。

my %h1 = 'apples' => 3'oranges' => 7;  # (no key type specified) 
say %h1.keyof;                           # OUTPUT: «(Str(Any))␤» 
 
my %h2{Str} = 'oranges' => 7;            # (keys must be of type Str) 
say %h2.keyof;                           # OUTPUT: «(Str)␤» 
%h2{3} = 'apples';                       # throws exception 
CATCH { default { put .^name''.Str } };
# OUTPUT: «X::TypeCheck::Binding: Type check failed in binding to key; expected Str but got Int (3)␤» 
 
my %h3{Int};                             # (this time, keys must be of type Int) 
%h3{42} = 4096;
say %h3.keyof;                           # OUTPUT: «(Int)␤»

method of§

method of(Hash:D:)

返回调用者的值的类型约束。默认情况下,即如果在声明期间未给出类型约束,该方法返回 (Mu)

my %h1 = 'apples' => 3'oranges' => 7;  # (no type constraint specified) 
say %h1.of;                              # OUTPUT: «(Mu)␤» 
 
my Int %h2 = 'oranges' => 7;             # (values must be of type Int) 
say %h2.of;                              # OUTPUT: «(Int)␤»

routine dynamic§

method dynamic(--> Bool:D)

如果调用者已使用 is dynamic 特性声明,则返回 True

my %a;
say %a.dynamic;                          # OUTPUT: «False␤» 
 
my %b is dynamic;
say %b.dynamic;                          # OUTPUT: «True␤»

如果您使用 * twigil 声明变量,则隐含 is dynamic

my %*b;
say %*b.dynamic;                         # OUTPUT: «True␤»

请注意,在 Scalar 情况下,您必须使用 VAR 方法才能获取正确的信息。

my $s is dynamic = %('apples' => 5);
say $s.dynamic;                   # OUTPUT: «False␤»  (wrong, don't do this) 
say $s.VAR.dynamic;               # OUTPUT: «True␤»   (correct approach)

下标副词§

一些方法作为下标上的副词实现(有关更多信息,请参阅 运算符 文档)。

:exists§

如果键存在于 Hash 中,副词 :exists 返回 Bool::True。如果提供了多个键,它将返回 ListBool

my %h = => 1=> 2;
say %h<a>:exists;   # OUTPUT: «True␤» 
say %h<a b>:exists# OUTPUT: «(True True)␤»

:delete§

使用 :deleteHash 中删除 Pair。此外,始终返回该值,但仅在删除为 true 时才执行删除。

my %h = => 1;
say %h;         # OUTPUT: «{a => 1}␤» 
say %h.elems;   # OUTPUT: «1␤» 
 
%h<a>:delete;
say %h;         # OUTPUT: «{}␤» 
say %h.elems;   # OUTPUT: «0␤»

:p§

副词 :p 返回 PairPair 的列表,而不仅仅是值。

my %h = => 1=> 2;
say %h<a>:p;    # OUTPUT: «a => 1␤» 
say %h<a b>:p;  # OUTPUT: «(a => 1 b=> 2)␤»

:v:k§

副词 :v:k 返回键或值或它们的列表。

my %h = => 1=> 2;
say %h<a>:k;    # OUTPUT: «a␤» 
say %h<a b>:k;  # OUTPUT: «(a b)␤»

副词 :kv 返回键和值的列表。

my %h = => 1=> 2=> 3;
say %h<a c>:kv;  # OUTPUT: «(a 1 c 3)␤»

您还可以在不知道哈希的情况下使用副词,方法是在其中使用空尖括号,在这种情况下将列出所有键和值

my %h1 = => 1;
my %h2 = => 1=> 2;
say %h1<>:k# OUTPUT: «(a)␤» 
say %h1<>:v# OUTPUT: «(1)␤» 
say %h2<>:k# OUTPUT: «(a b)␤» 
say %h2<>:v# OUTPUT: «(1 2)␤»

类型图§

Hash 的类型关系
raku-type-graph Hash Hash Map Map Hash->Map Mu Mu Any Any Any->Mu Cool Cool Cool->Any Iterable Iterable Associative Associative Map->Cool Map->Iterable Map->Associative Stash Stash Stash->Hash

展开上面的图表