I created the following slice:
my %hash = ( a => 1, b => 2, c => 3 );
my #backslashed_slice_result = \#hash{qw(a b c)};
# $backslashed... <-- originally I wrote this is a scalar, see below.
I expected this to produce an array reference to an array populated by the hash slice, equivalent to:
my $arrayref_containing_slice = [ #hash{qw(a b c)} ]; # [1, 2, 3]
But actually is doing something more like:
my #mapped_list_of_values_to_refs = map { \$hash{$_} } qw(a b c); # (\1, \2, \3)
The main point I could find in the documentation that describes this behaviour is the statement:
A slice accesses several elements of a list, an array, or a hash
simultaneously using a list of subscripts.
So is \#hash{qw(a b c)} actually a list slice, rather than a hash slice? I am surprised at the reference being taken of the values of the hash, instead of of the resulting array of values. What is it about the syntax that causes perl to apply the leading \ to the values of the hash?
Two points:
First, a hash slice does not produce an array, it produces a list of the sliced hash elements.
Second, using \ on a list produces a list of references to the elements of the first list.
Related
I am having issues accessing the value of a 2-dimensional hash. From what I can tell online, it should be something like: %myHash{"key1"}{"key2"} #Returns value
However, I am getting the error: "Type Array does not support associative indexing."
Here's a Minimal Reproducible Example.
my %hash = key1-dim1 => key1-dim2 => 42, key2-dim1 => [42, 42];
say %hash{'key1-dim1'}{'key1-dim2'}; # 42
say %hash{'key2-dim1'}{'foo bar'}; # Type Array does not support associative indexing.
Here's another reproducible example, but longer:
my #tracks = 'Foo Bar', 'Foo Baz';
my %count;
for #tracks -> $title {
$_ = $title;
my #words = split(/\s/, $_);
if (#words.elems > 1) {
my $i = 0;
while (#words.elems - $i > 1) {
my %wordHash = ();
%wordHash.push: (#words[$i + 1] => 1);
%counts.push: (#words[$i] => %wordHash);
say %counts{#words[$i]}{#words[$i+1]}; #===============CRASHES HERE================
say %counts.kv;
$i = $i + 1;
}
}
}
In my code above, the problem line where the 2-d hash value is accessed will work once in the first iteration of the for-loop. However, it always crashes with that error on the second time through. I've tried replacing the array references in the curly braces with static key values in case something was weird with those, but that did not affect the result. I can't seem to find what exactly is going wrong by searching online.
I'm very new to raku, so I apologize if it's something that should be obvious.
After adding the second elements with push to the same part of the Hash, the elment is now an array. Best you can see this by print the Hash before the crash:
say "counts: " ~ %counts.raku;
#first time: counts: {:aaa(${:aaa(1)})}
#second time: counts: {:aaa($[{:aaa(1)}, {:aaa(1)}])}
The square brackets are indicating an array.
Maybe BagHash does already some work for you. See also raku sets without borders
my #tracks = 'aa1 aa2 aa2 aa3', 'bb1 bb2', 'cc1';
for #tracks -> $title {
my $n = BagHash.new: $title.words;
$n.raku.say;
}
#("aa2"=>2,"aa1"=>1,"aa3"=>1).BagHash
#("bb1"=>1,"bb2"=>1).BagHash
#("cc1"=>1).BagHash
Let me first explain the minimal example:
my %hash = key1-dim1 => key1-dim2 => 42,
key2-dim1 => [42, 42];
say %hash{'key1-dim1'}{'key1-dim2'}; # 42
say %hash{'key2-dim1'}{'key2-dim2'}; # Type Array does not support associative indexing.
The problem is that the value associated with key2-dim1 isn't itself a hash but is instead an Array. Arrays (and all other Positionals) only support indexing by position -- by integer. They don't support indexing by association -- by string or object key.
Hopefully that explains that bit. See also a search of SO using the [raku] tag plus 'Type Array does not support associative indexing'.
Your longer example throws an error at this line -- not immediately, but eventually:
say %counts{...}{...}; # Type Array does not support associative indexing.
The hash %counts is constructed by the previous line:
%counts.push: ...
Excerpting the doc for Hash.push:
If a key already exists in the hash ... old and new value are both placed into an Array
Example:
my %h = a => 1;
%h.push: (a => 1); # a => [1,1]
Now consider that the following code would have the same effect as the example from the doc:
my %h;
say %h.push: (a => 1); # {a => 1}
say %h.push: (a => 1); # {a => [1,1]}
Note how the first .push of a => 1 results in a 1 value for the a key of the %h hash, while the second .push of the same pair results in a [1,1] value for the a key.
A similar thing is going on in your code.
In your code, you're pushing the value %wordHash into the #words[$i] key of the %counts hash.
The first time you do this the resulting value associated with the #words[$i] key in %counts is just the value you pushed -- %wordHash. This is just like the first push of 1 above resulting in the value associated with the a key, from the push, being 1.
And because %wordHash is itself a hash, you can associatively index into it. So %counts{...}{...} works.
But the second time you push a value to the same %counts key (i.e. when the key is %counts{#words[$i]}, with #words[$i] set to a word/string/key that is already held by %counts), then the value associated with that key will not end up being associated with %wordHash but instead with [%wordHash, %wordHash].
And you clearly do get such a second time in your code, if the #tracks you are feeding in have titles that begin with the same word. (I think the same is true even if the duplication isn't the first word but instead later ones. But I'm too confused by your code to be sure what the exact broken combinations are. And it's too late at night for me to try understand it, especially given that it doesn't seem important anyway.)
So when your code then evaluates %counts{#words[$i]}{#words[$i+1]}, it is the same as [%wordHash, %wordHash]{...}. Which doesn't make sense, so you get the error you see.
Hopefully the foregoing has been helpful.
But I must say I'm both confused by your code, and intrigued as to what you're actually trying to accomplish.
I get that you're just learning Raku, and that what you've gotten from this SO might already be enough for you, but Raku has a range of nice high level hash like data types and functionality, and if you describe what you're aiming at we might be able to help with more than just clearing up Raku wrinkles that you and we have been dealing with thus far.
Regardless, welcome to SO and Raku. :)
Well, this one was kind of funny and surprising. You can't go wrong if you follow the other question, however, here's a modified version of your program:
my #tracks = ['love is love','love is in the air', 'love love love'];
my %counts;
for #tracks -> $title {
$_ = $title;
my #words = split(/\s/, $_);
if (#words.elems > 1) {
my $i = 0;
while (#words.elems - $i > 1) {
my %wordHash = ();
%wordHash{#words[$i + 1]} = 1;
%counts{#words[$i]} = %wordHash;
say %counts{#words[$i]}{#words[$i+1]}; # The buck stops here
say %counts.kv;
$i = $i + 1;
}
}
}
Please check the line where it crashed before. Can you spot the difference? It was kind of a (un)lucky thing that you used i as a loop variable... i is a complex number in Raku. So it was crashing because it couldn't use complex numbers to index an array. You simply had dropped the $.
You can use sigilless variables in Raku, as long as they're not i, or e, or any of the other constants that are already defined.
I've also made a couple of changes to better reflect the fact that you're building a Hash and not an array of Pairs, as Lukas Valle said.
I keep record of how many times a letter occur in a word e.g. 'embeddedss'
my %x := {e => 3, m => 1, b => 1, d => 3, s => 2};
I'd like to print the elements by grouping their values like this:
# e and d 3 times
# m and b 1 times
# s 2 times
How to do it practically i.e. without constructing loops (if there is any)?
Optional Before printing the hash, I'd like to convert and assing it to a temporary data structure such as ( <3 e d>, <1 m b>, <2 s> ) and then print it. What could be the most practical data structure and way to print it?
Using .categorize as suggested in the comments, you can group them together based on the value.
%x.categorize(*.value)
This produces a Hash, with the keys being the value used for categorization, and the values being Pairs from your original Hash ($x). This can be looped over using for or .map. The letters you originally counted are the key of the Pairs, which can be neatly extracted using .map.
for %x.categorize(*.value) {
say "{$_.value.map(*.key).join(' and ')} {$_.key} times";
}
Optionally, you can also sort the List by occurrences using .sort. This will sort the lowest number first, but adding a simple .reverse will make the highest value come first.
for %x.categorize(*.value).sort.reverse {
say "{$_.value.map(*.key).join(' and ')} {$_.key} times";
}
Suppose I use containers map to create a dictionary in MATLAB which has the following map:
1-A;
2-B;
3-C;
Denote the dictionary as D.
Now I have an input list [2,1,3], and what I am expecting is [B,A,C]. The problem is, I can't just use [2,1,3] as the input list for D, but only input 2,1 and 3 one by one for D and get B, A, C each time.
This can get the job done but as you can see, it's a bit less efficient.
So my question is: is there anything else I can do to let the dictionary return the whole list at the same time?
As far as I can find there is no one-step solution like python's dict.items. You can, however, get in a few lines. mydict.keys() gives you the keys of the dict as a cell array, and mydict.values() gives you the values as a cell array, so you can (in theory) combine those:
>> mykeys = mydict.keys();
>> myvals = mydict.values();
>> mypairs = [mykeys',myvals']
mypairs =
3×2 cell array
'A' [1]
'B' [2]
'C' [3]
However, in principle maps are unordered, and I can't find anything in the MATLAB documentation that says that the order returns by keys and the order returned by values is necessarily consistent (unlike Python). So if you want to be extra safe, you can call values with a cell array of the keys you want, which in this case would be all the keys:
>> mykeys = mydict.keys();
>> myvals = mydict.values(mykeys);
>> mypairs = [mykeys',myvals']
mypairs =
3×2 cell array
'A' [1]
'B' [2]
'C' [3]
I read about scalars arrays and list. I am not sure what is meant by list. For example, (5, apple, $x, 3.14) is a list, but what is the variable actually referencing the list?
Are lists just a way to initialize arrays or is it the known data structure?
How do you reference a list in Perl?
The same as you do with anything else - with the reference operator \. See perlreftut and perlref.
my $x;
\(5, 'apple', $x, 3.14)
The expression returns
(
\ 5,
\ "apple",
\ undef,
\ 3.14
)
which is not that useful.
For example, (5, apple, $x, 3.14) is a list, but what is the variable actually referencing the list?
There is none. It is an anonymous value.
Not every value needs a variable to hold it - literals are also an important part of programs.
Are lists just a way to initialize arrays or is it the known data structure?
Read Perl array vs list.
There are three different basic data structures in Perl.
Hashes: These are key/value pairs.
Arrays: These are ordered values.
Scalars: This is a single value.
Hashes:
my %employee = (
"A001" => "Albert",
"A002" => "Bob",
"B003" => "Gary",
);
print "$employee{A001}\n"; #Prints "Albert"
print "$employee{B003}\n"; #Prints "Gary"
Arrays:
my #fruit = ("Apple", "Orange", "Banana");
print "$fruit[0]\n"; #Prints "Apple"
print "$fruit[2]\n"; #Prints "Banana"
Scalars:
my $age = "None of your business";
print "You're $age years old\n"; #Prints "You're None of your business years old
A List is merely a list of items. For example, #fruit is an array. However, I set my array to equal a list containing the names of fruits.
Technically, a list is unchangeable while an array is a data structure that can be modified.
for my $color ("red", "green", "blue") {
print "$color is an item from my list\n";
}
In the above, my for loop advances through a list:
my #list = ("red", "green", "blue");
for my $color (#list) {
print "$color is a member of my array\n";
}
The above is pretty much the same code, but now my for loop advances through an array.
On a really basic level I think understanding what an operator is helps.
This can be summed up by saying anything which is not a value, is an operator.
What is the list operator?
,
How does the list operator work?
The list operator evaluates the value on the left, then evaluates the value on the right. If the value on the right of itself also has a list operator, the associativity of the operator means the evaluations will continue until no more list operators appear or end of line.
#!usr/bin/perl
use warnings;
use strict;
my #namedlist = 1, my #namedlist2 = 2;
print #namedlist , #namedlist2;
12
Once you understand the list operator, you can then deal with the real issue of your problem:
context
For example, print supplies list context to its arguments. It expects a list, not a singular value, so it supplies the list context.
Now we know that the [], and {} operators, are the reference to an anonymous array constructor, and reference to anonymous hash constructor respectively, but I think few realise that before these there was only the list constructor operator. The list constructor operator is
()
Why does this matter? Well, because the list operator , does not know about context. It just evaluates and discards, evaluates and discards.
Supply the context by wrapping a list operator with a list constructor (), and now the list constructor captures the evaluations of the list operator so that it can return a list.
Of course you will be using the assignment operator = to assign the newly constructed list returned by the list constructor (), to a named list holder. Such as an array or a hash.
my #list = (one, 1, two, 2);
my %associatedlist = (three, 3, four, 4);
So a list is a series of evaluated values captured by the list constructor, and then usually assigned into a named list holder.
This is apparent through the reverse engineering of the reference to list constructors, [] and {}.
There being a difference between the two reference to list constructors, highlights their use as operators, perhaps descending from earlier usage of a similar operator?
The reference to list constructers, must be explicitly stated through the use of either the reference to the anonymous array constructor [], or reference to the anonymous hash constructor {}.
The list constructer () is constructing a list of values which are then generally assigned to either a named array or named associated array. Though the assignment operator = does not distinguish between its leftvalue and rightvalue, it just attempts to assign the right to the left, if the contexts are incompatible or the list sizes are of differing lengths, it then proceeds to compliment your handiwork to STDOUT accordingly.
useless use of constant in void context at roughly 1 foot from keyboard.pl
What is the shortest amount of code to modify a hash within a hash in the following instances:
%hash{'a'} = { 1 => one,
2 => two };
(1) Add a new key to the inner hash of 'a' (ex: c => 4 in the inner hash of 'a')
(2) Changing a value in the inner hash (ex: change the value of 1 to 'ONE')
Based on the question, you seem new to perl, so you should look at perldoc perlop among others.
Your %hash keys contain scalar values that are hashrefs. You can dereference using the -> operator, eg, $hashref = {foo=>42}; $hashref->{foo}. Similarly you can do the same with the values in the hash: $hash{a}->{1}. When you chain the indexes, though, there's some syntactic sugar for an implicit -> between them, so you can just do $hash{a}{1} = 'ONE' and so on.
This question probably also will give you some useful leads.
$hash{a}{c} = 4;
$hash{a}{1} = "ONE";