/
BagHash.pod6
150 lines (109 loc) · 5.36 KB
/
BagHash.pod6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
=begin pod
=TITLE class BagHash
=SUBTITLE Mutable collection of distinct objects with integer weights
class BagHash does Baggy { }
A C<BagHash> is a mutable bag/multiset, meaning a collection of distinct
elements in no particular order that each have an integer weight assigned to
them signifying how many copies of that element are considered "in the bag".
(For I<immutable> bags, see L<Bag|/type/Bag> instead.)
Objects/values of any type are allowed as bag elements. Within a C<BagHash>,
items that would compare positively with the L<===> operator are considered the
same element, with the number of how many there were as its weight. But of
course you can also easily get back the expanded list of items (without the
order):
=begin code
my $breakfast = <spam eggs spam spam bacon spam>.BagHash;
say $breakfast.elems; # OUTPUT: «3»
say $breakfast.keys.sort; # OUTPUT: «bacon eggs spam»
say $breakfast.total; # OUTPUT: «6»
say $breakfast.kxxv.sort; # OUTPUT: «bacon eggs spam spam spam spam»
=end code
C<BagHash>es can be treated as object hashes using the C<{ }> postcircumfix
operator, which returns the corresponding integer weight for keys that are
elements of the bag, and C<0> for keys that aren't. It can also be used to
modify weights; setting a weight to C<0> automatically removes that element from
the bag, and setting a weight to a positive number adds that element if it
didn't already exist:
=begin code
my $breakfast = <spam eggs spam spam bacon spam>.BagHash;
say $breakfast<bacon>; # OUTPUT: «1»
say $breakfast<spam>; # OUTPUT: «4»
say $breakfast<sausage>; # OUTPUT: «0»
$breakfast<sausage> = 2;
$breakfast<bacon>--;
say $breakfast.kxxv.sort; # OUTPUT: «eggs sausage sausage spam spam spam spam»
=end code
=head1 Creating C<BagHash> objects
C<BagHash>es can be composed using C<BagHash.new>. Any positional parameters,
regardless of their type, become elements of the bag:
my $n = BagHash.new: "a", "b", "c", "c";
say $n.perl; # OUTPUT: «("b"=>1,"a"=>1,"c"=>2).BagHash»
say $n.keys.perl; # OUTPUT: «("b", "a", "c").Seq»
say $n.values.perl; # OUTPUT: «(1, 1, 2).Seq»
Besides, C<BagHash.new-from-pairs> can create a C<BagHash> with items and their
occurrences.
my $n = BagHash.new-from-pairs: "a" => 0, "b" => 1, "c" => 2, "c" => 2;
say $n.perl; # OUTPUT: «("b"=>1,"c"=>4).BagHash»
say $n.keys.perl; # OUTPUT: «("b", "c").Seq»
say $n.values.perl; # OUTPUT: «(1, 4).Seq»
Alternatively, the C<.BagHash> coercer (or its functional form, C<BagHash()>)
can be called on an existing object to coerce it to a C<BagHash>. Its semantics
depend on the type and contents of the object. In general it evaluates the
object in list context and creates a bag with the resulting items as elements,
although for Hash-like objects or Pair items, only the keys become elements of
the bag, and the (cumulative) values become the associated integer weights:
my $m = ("a", "b", "c", "c").BagHash;
my $n = ("a" => 0, "b" => 1, "c" => 2, "c" => 2).BagHash;
say $m.perl; # OUTPUT: «("b"=>1,"a"=>1,"c"=>2).BagHash»
say $n.perl; # OUTPUT: «("b"=>1,"c"=>4).BagHash»
You can also create C<BagHash> masquerading as a hash by using the C<is> trait:
my %bh is BagHash = <a b b c c c>;
say %bh<b>; # 2
say %bh<d>; # 0
Since 6.d (2019.01 and later) it is also possible to specify the type of values
you would like to allow in a C<BagHash>. This can either be done when calling
C<.new>:
# only allow strings
my $n = BagHash[Str].new: <a b b c c c>;
or using the masquerading syntax:
# only allow strings
my %bh is BagHash[Str] = <a b b c c c>;
say %bh<b>; # 2
say %bh<d>; # 0
# only allow whole numbers
my %bh is BagHash[Int] = <a b b c c c>;
# Type check failed in binding; expected Int but got Str ("a")
=head1 Operators
Perl 6 provides common set and bag operators, which can take C<BagHash>es (or
any other collections) as input, and return result as C<Bool>, C<Set> or C<Bag>
values. For example:
=begin code
my ($a, $b) = BagHash.new(2, 2, 4), BagHash.new(2, 3, 3, 4);
say $a (<) $b; # OUTPUT: «False»
say $a (<+) $b; # OUTPUT: «False»
say $a (^) $b; # OUTPUT: «Bag(3(2), 2)»
say $a (+) $b; # OUTPUT: «Bag(2(3), 4(2), 3(2))»
# Unicode versions:
say $a ⊂ $b; # OUTPUT: «False»
say $a ≼ $b; # OUTPUT: «False»
say $a ⊖ $b; # OUTPUT: «Bag(3(2), 2)»
say $a ⊎ $b; # OUTPUT: «Bag(2(3), 4(2), 3(2))»
=end code
See L<Set/Bag Operators|/language/setbagmix#Set/Bag_operators> for a complete list of
set and bag operators with detailed explanations.
=head1 Note on C<reverse> and ordering.
BagHash inherits C<reverse> from L<Any|/type/Any#routine_reverse>,
however, C<Bag>s do not have an inherent order and you should not trust
it returning a consistent output.
If you sort a BagHash, the result is a list of pairs, at which point
C<reverse> makes perfect sense:
=begin code
my $a = BagHash.new(2, 2, 18, 3, 4);
say $a; # OUTPUT: «BagHash(18, 2(2), 3, 4)»
say $a.sort; # OUTPUT: «(2 => 2 3 => 1 4 => 1 18 => 1)»
say $a.sort.reverse; # OUTPUT: «(18 => 1 4 => 1 3 => 1 2 => 2)»
=end code
=head1 See Also
L<Sets, Bags, and Mixes|/language/setbagmix>
=end pod
# vim: expandtab softtabstop=4 shiftwidth=4 ft=perl6