Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hash with Array values in Perl 6

Tags:

raku

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
like image 837
Eugene Barsky Avatar asked May 11 '18 20:05

Eugene Barsky


People also ask

Can Perl hash value array?

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.

How do you handle an array and hash in Perl?

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 @ .

How do I declare an array of hash in Perl?

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 (%).

How do you add a value to a hash in Perl?

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.)


2 Answers

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.

like image 108
Jonathan Worthington Avatar answered Oct 20 '22 18:10

Jonathan Worthington


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.

like image 35
raiph Avatar answered Oct 20 '22 16:10

raiph