I'm trying to make a Hash
with non-string keys, in my case arrays or lists.
> my %sum := :{(1, 3, 5) => 9, (2, 4, 6) => 12}
{(1 3 5) => 9, (2 4 6) => 12}
Now, I don't understand the following.
How to retrieve an existing element?
> %sum{(1, 3, 5)}
((Any) (Any) (Any))
> %sum{1, 3, 5}
((Any) (Any) (Any))
How to add a new element?
> %sum{2, 4} = 6
(6 (Any))
Several things are going on here: first of all, if you use
(1,2,3)
as a key, Rakudo Perl 6 will consider this to be a slice of 3 keys:1
,2
and3
. Since neither of these exist in the object hash, you get((Any) (Any) (Any))
.So you need to indicate that you want the list to be seen as single key of which you want the value. You can do this with
$()
, so%sum{$(1,3,5)}
. This however does not give you the intended result. The reason behind that is the following:Object hashes internally key the object to its
.WHICH
value. At the moment,List
s are not considered value types, so eachList
has a different.WHICH
. Which makes them unfit to be used as keys in object hashes, or in other cases where they are used by default (e.g..unique
andSet
s,Bag
s andMix
es).I'm actually working on making this the above
eq
returnTrue
before long: this should make it to the 2018.01 compiler release, on which also a Rakudo Star release will be based.BTW, any time you're using object hashes and integer values, you will probably be better of using
Bag
s. Alas not yet in this case either for the above reason.You could actually make this work by using
augment class List
and adding a.WHICH
method on that, but I would recommend against that as it will interfere with any future fixes.Elizabeth's answer is solid, but until that feature is created, I don't see why you can't create a
Key
class to use as the hash key, which will have an explicit hash function which is based on its values rather than its location in memory. This hash function, used for both placement in the list and equality testing, is.WHICH
. This function must return anObjAt
object, which is basically just a string.Note that I only allowed the key to contain
Int
. This is an easy way of being fairly confident no element's string value contains the '|' character, which could make two keys look the same despite having different elements. However, this is not hardened against naughty users--4 but role :: { method Str() { '|' } }
is anInt
that stringifies to the illegal value. You can make the code stronger if you use.WHICH
recursively, but I'll leave that as an exercise.This
Key
class is also a little fancier than you strictly need. It would be enough to have a@.list
member and define.WHICH
. I defined AT-POS and friends so theKey
can be indexed, pushed to, and otherwise treated as anArray
.