What's going on here?
Why are %a{3}
and %a{3}.Array
different if %a
has Array
values and %a{3}
is an Array
?
> my Array %a
{}
> %a{3}.push("foo")
[foo]
> %a{3}.push("bar")
[foo bar]
> %a{3}.push("baz")
[foo bar baz]
> .say for %a{3}
[foo bar baz]
> %a{3}.WHAT
(Array)
> .say for %a{3}.Array
foo
bar
baz
Elements of hash can be anything, including references to array. For example what if you have a bunch of people and each person has a list of scores. Another interesting example would be a bunch of people each person belonging to 1 or more groups.
To assign some values to the array, just enclose them in parentheses. You can retrieve them with the index number. Notice how the @ changed to a $ for the print statement; I wanted it to return a scalar, a single thing, not a list of things. If you want to do things to the whole array, use the @ .
A Perl hash is defined by key-value pairs. Perl stores elements of a hash in such an optimal way that you can look up its values based on keys very fast. Like a scalar or an array variable, a hash variable has its own prefix. A hash variable must begin with a percent sign (%).
To add more elements to the Perl hash, just use that same syntax over and over, like this: $prices{'coke'} = 1.25; $prices{'sandwich'} = 3.00; (Note that there is no "Perl push" syntax for adding a new element to a Perl hash, but because people ask me that so many times, I wanted to make sure I mentioned it here.)
The difference being observed here is the same as with:
my $a = [1,2,3];
.say for $a; # [1 2 3]
.say for $a.Array; # 1\n2\n3\n
The $
sigil can be thought of as meaning "a single item". Thus, when given to for
, it will see that and say "aha, a single item" and run the loop once. This behavior is consistent across for
and operators and routines. For example, here's the zip operator given arrays and them itemized arrays:
say [1, 2, 3] Z [4, 5, 6]; # ((1 4) (2 5) (3 6))
say $[1, 2, 3] Z $[4, 5, 6]; # (([1 2 3] [4 5 6]))
By contrast, method calls and indexing operations will always be called on what is inside of the Scalar
container. The call to .Array
is actually a no-op since it's being called on an Array
already, and its interesting work is actually in the act of the method call itself, which is unwrapping the Scalar
container. The .WHAT
is like a method call, and is telling you about what's inside of any Scalar
container.
The values of an array and a hash are - by default - Scalar
containers which in turn hold the value. However, the .WHAT
used to look at the value was hiding that, since it is about what's inside the Scalar
. By contrast, .perl
[1] makes it clear that there's a single item:
my Array %a;
%a{3}.push("foo");
%a{3}.push("bar");
say %a{3}.perl; $["foo", "bar"]
There are various ways to remove the itemization:
%a{3}.Array # Identity minus the container
%a{3}.list # Also identity minus the container for Array
@(%a{3}) # Short for %a{3}.cache, which is same as .list for Array
%a{3}<> # The most explicit solution, using the de-itemize op
|%a{3} # Short for `%a{3}.Slip`; actually makes a Slip
I'd probably use for %a{3}<> { }
in this case; it's both shorter than the method calls and makes clear that we're doing this purely to remove the itemization rather than a coercion.
While for |%a{3} { }
also works fine and is visually nice, it is the only one that doesn't optimize down to simply removing something from its Scalar
container, and instead makes an intermediate Slip
object, which is liable to slow the iteration down a bit (though depending on how much work is being done by the loop, that could well be noise).
[1] Based on what I wrote, one may wonder why .perl
can recover the fact that something was itemized. A method call $foo.bar
is really doing something like $foo<>.^find_method('bar')($foo)
. Then, in a method bar() { self }
, the self
is bound to the thing the method was invoked on, removed from its container. However, it's possible to write method bar(\raw-self:) { }
to recover it exactly as it was provided.
The issue is Scalar
containers do DWIM indirection.
%a{3}
is bound to a Scalar
container.
By default, if you refer to the value or type of a Scalar
container, you actually access the value, or type of the value, contained in the container.
In contrast, when you refer to an Array
container as a single entity, you do indeed access that Array
container, no sleight of hand.
To see what you're really dealing with, use .VAR
which shows what a variable (or element of a composite variable) is bound to rather than allowing any container it's bound to to pretend it's not there.
say %a{3}.VAR ; # $["foo", "bar", "baz"]
say %a{3}.Array.VAR ; # [foo bar baz]
This is a hurried explanation. I'm actually working on a post specifically focusing on containers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With