Why isn't my sort working in Perl? - perl

I have never used Perl, but I need to complete this exercise. My task is to sort an array in a few different ways. I've been provided with a test script. This script puts together the array and prints statements for each stage of it's sorting. I've named it foo.pl:
use strict;
use warnings;
use MyIxHash;
my %myhash;
my $t = tie(%myhash, "MyIxHash", 'a' => 1, 'abe' => 2, 'cat'=>'3');
$myhash{b} = 4;
$myhash{da} = 5;
$myhash{bob} = 6;
print join(", ", map { "$_ => $myhash{$_}" } keys %myhash) . " are the starting key => val pairs\n";
$t->SortByKey; # sort alphabetically
print join(", ", map { "$_ => $myhash{$_}" } keys %myhash) . " are the alphabetized key => val pairs\n";
$t->SortKeyByFunc(sub {my ($a, $b) = #_; return ($b cmp $a)}); # sort alphabetically in reverse order
print join(", ", map { "$_ => $myhash{$_}" } keys %myhash) . " are the reverse alphabetized key => val pairs\n";
$t->SortKeyByFunc(\&abcByLength); # use abcByLength to sort
print join(", ", map { "$_ => $myhash{$_}" } keys %myhash) . " are the abcByLength sorted key => val pairs\n";
print "Done\n\n";
sub abcByLength {
my ($a, $b) = #_;
if(length($a) == length($b)) { return $a cmp $b; }
else { return length($a) <=> length($b) }
}
Foo.pl uses a package called MyIxHash which I've created a module for called MyIxHash.pm. The script runs through the alphabetical sort: "SortByKey", which I've inherited via the "IxHash" package in my module. The last two sorts are the ones giving me issues. When the sub I've created: "SortKeyByFunc" is ran on the array, it passes in the array and a subroutine as arguments. I've attempted to take those arguments and associate them with variables.
The final sort is supposed to sort by string length, then alphabetically. A subroutine for this is provided at the bottom of foo.pl as "abcByLength". In the same way as the reverse alpha sort, this subroutine is being passed as a parameter to my SortKeyByFunc subroutine.
For both of these sorts, it seems the actual sorting work is done for me, and I just need to apply this subroutine to my array.
My main issue here seems to be that I don't know how, if possible, to take my subroutine argument and run my array through it as a parameter. I'm a running my method on my array incorrectly?
package MyIxHash;
#use strict;
use warnings;
use parent Tie::IxHash;
use Data::Dumper qw(Dumper);
sub SortKeyByFunc {
#my $class = shift;
my ($a, $b) = #_;
#this is a reference to the already alphabetaized array being passed in
my #letters = $_[0][1];
#this is a reference to the sub being passed in as a parameter
my $reverse = $_[1];
#this is my variable to contain my reverse sorted array
my #sorted = #letters->$reverse();
return #sorted;
}
1;

"My problem occurs where I try: my #sorted = #letters->$reverse(); I've also tried: my #sorted = sort {$reverse} #letters;"
You were really close; the correct syntax is:
my $reverse = sub { $b cmp $a };
# ...
my #sorted = sort $reverse #letters;
Also note that, for what are basically historical reasons, sort passes the arguments to the comparison function in the (slightly) magic globals $a and $b, not in #_, so you don't need to (and indeed shouldn't) do my ($a, $b) = #_; in your sortsubs (unless you declare them with a prototype; see perldoc -f sort for the gritty details).
Edit: If you're given a comparison function that for some reason does expect its arguments in #_, and you can't change the definition of that function, then your best bet is probably to wrap it in a closure like this:
my $fixed_sortsub = sub { $weird_sortsub->($a, $b) };
my #sorted = sort $fixed_sortsub #letters;
or simply:
my #sorted = sort { $weird_sortsub->($a, $b) } #letters;
Edit 2: Ah, I see the/a problem. When you write:
my #letters = $_[0][1];
what you end up with a is a single-element array containing whatever $_[0][1] is, which is presumably an array reference. You should either dereference it immediately, like this:
my #letters = #{ $_[0][1] };
or just keep is as a reference for now and dereference it when you use it:
my $letters = $_[0][1];
# ...
my #sorted = sort $whatever #$letters;
Edit 3: Once you do manage to sort the keys, then, as duskwuff notes in his original answer, you'll also need to call the Reorder() method from your parent class, Tie::IxHash to actually change the order of the keys. Also, the first line:
my ($a, $b) = #_;
is completely out of place in what's supposed to be an object method that takes a code reference (and, in fact, lexicalizing $a and $b is a bad idea anyway if you want to call sort later in the same code block). What it should read is something like:
my ($self, $sortfunc) = #_;
In fact, rather than enumerating all the things that seem to be wrong with your original code, it might be easier to just fix it:
package MyIxHash;
use strict;
use warnings;
use parent 'Tie::IxHash';
sub SortKeyByFunc {
my ($self, $sortfunc) = #_;
my #unsorted = $self->Keys();
my #sorted = sort { $sortfunc->($a, $b) } #unsorted;
$self->Reorder( #sorted );
}
1;
or simply:
sub SortKeyByFunc {
my ($self, $sortfunc) = #_;
$self->Reorder( sort { $sortfunc->($a, $b) } $self->Keys() );
}
(Ps. I now see why the comparison functions were specified as taking their arguments in #_ rather than in the globals $a and $b where sort normally puts them: it's because the comparison functions belong to a different package, and $a and $b are not magical enough to be the same in every package like, say, $_ and #_ are. I guess that could be worked around, but it would take some quite non-trivial trickery with caller.)
(Pps. Please do credit me and duskwuff / Stack Overflow when you hand in your exercise. And good luck with learning Perl — trust me, it'll be a useful skill to have.)

Your SortKeyByFunc method returns the results of sorting the array (#sorted), but it doesn't modify the array "in place". As a result, just calling $t->SortKeyByFunc(...); doesn't end up having any visible permanent effects.
You'll need to call $t->Reorder() within your SortKeyByFunc method to have any lasting impact on the array. I haven't tried it, but something like:
$t->Reorder(#sorted);
at the end of your method may be sufficient.

Related

Is there a simple way to validate a hash of hash element comparsion?

Is there a simple way to validate a hash of hash element comparsion ?
I need to validate a Perl hash of hash element $Table{$key1}{$key2}{K1}{Value} compare to all other elements in hash
third key will be k1 to kn and i want comprare those elements and other keys are same
if ($Table{$key1}{$key2}{K1}{Value} eq $Table{$key1}{$key2}{K2}{Value}
eq $Table{$key1}{$key2}{K3}{Value} )
{
#do whatever
}
Something like this may work:
use List::MoreUtils 'all';
my #keys = map "K$_", 1..10;
print "All keys equal"
if all { $Table{$key1}{$key2}{$keys[1]}{Value} eq $Table{$key1}{$key2}{$_}{Value} } #keys;
I would use Data::Dumper to help with a task like this, especially for a more general problem (where the third key is more arbitrary than 'K1'...'Kn'). Use Data::Dumper to stringify the data structures and then compare the strings.
use Data::Dumper;
# this line is needed to assure that hashes with the same keys output
# those keys in the same order.
$Data::Dumper::Sortkeys = 1;
my $string1= Data::Dumper->Dump($Table{$key1}{$key2}{k1});
for ($n=2; exists($Table{$key1}{$key2}{"k$n"}; $n++) {
my $string_n = Data::Dumper->Dump($Table{$key1}{$key2}{"k$n"});
if ($string1 ne $string_n) {
warn "key 'k$n' is different from 'k1'";
}
}
This can be used for the more general case where $Table{$key1}{$key2}{k7}{value} itself contains a complex data structure. When a difference is detected, though, it doesn't give you much help figuring out where that difference is.
A fairly complex structure. You should be looking into using object oriented programming techniques. That would greatly simplify your programming and the handling of these complex structures.
First of all, let's simplify a bit. When you say:
$Table{$key1}{$key2}{k1}{value}
Do you really mean:
my $value = $Table{$key1}->{$key2}->{k1};
or
my $actual_value = $Table{$key1}->{$key2}->{k1}->{Value};
I'm going to assume the first one. If I'm wrong, let me know, and I'll update my answer.
Let's simplify:
my %hash = %{$Table{$key1}->{$key2}};
Now, we're just dealing with a hash. There are two techniques you can use:
Sort the keys of this hash by value, then if two keys have the same value, they will be next to each other in the sorted list, making it easy to detect duplicates. The advantage is that all the duplicate keys would be printed together. The disadvantage is that this is a sort which takes time and resources.
Reverse the hash, so it's keyed by value and the value of that key is the key. If a key already exists, we know the other key has a duplicate value. This is faster than the first technique because no sorting is involved. However, duplicates will be detected, but not printed together.
Here's the first technique:
my %hash = %{$Table{$key1}->{$key2}};
my $previous_value;
my $previous_key;
foreach my $key (sort {$hash{$a} cmp $hash{$b}} keys %hash) {
if (defined $previous_key and $previous_value eq $hash{$key}) {
print "\$hash{$key} is a duplicate of \$hash{$previous_key}\n";
}
$previous_value = $hash{$key};
$previous_key = $key;
}
And the second:
my %hash = %{$Table{$key1}->{$key2}};
my %reverse_hash;
foreach $key (keys %hash) {
my $value = $hash{$key};
if (exists $reverse_hash{$value}) {
print "\$hash{$reverse_hash{$value}} has the same value as \$hash{$key}\n";
}
else {
$reverse_hash{$value} = $key;
}
}
Alternative approach to the problem is make utility function which will compare all keys if has same value returned from some function for all keys:
sub AllSame (&\%) {
my ($c, $h) = #_;
my #k = keys %$h;
my $ref;
$ref = $c->() for $h->{shift #k};
$ref ne $c->() and return for #$h{#k};
return 1
}
print "OK\n" if AllSame {$_->{Value}} %{$Table{$key1}{$key2}};
But if you start thinking in this way you can found this approach much more generic (recommended way):
sub AllSame (#) {
my $ref = shift;
$ref ne $_ and return for #_;
return 1
}
print "OK\n" if AllSame map {$_->{Value}} values %{$Table{$key1}{$key2}};
If mapping operation is expensive you can make lazy counterpart of same:
sub AllSameMap (&#) {
my $c = shift;
my $ref;
$ref = $c->() for shift;
$ref ne $c->() and return for #_;
return 1
}
print "OK\n" if AllSameMap {$_->{Value}} values %{$Table{$key1}{$key2}};
If you want only some subset of keys you can use hash slice syntax e.g.:
print "OK\n" if AllSame map {$_->{Value}} #{$Table{$key1}{$key2}}{map "K$_", 1..10};

How is the map function in Perl implemented?

Is map function in Perl written in Perl? I just can not figure out how to implement it. Here is my attempt:
use Data::Dumper;
sub Map {
my ($function, $sequence) = #_;
my #result;
foreach my $item (#$sequence) {
my $_ = $item;
push #result, $function->($item);
}
return #result
}
my #sample = qw(1 2 3 4 5);
print Dumper Map(sub { $_ * $_ }, \#sample);
print Dumper map({ $_ * $_ } #sample);
$_ in $function is undefined as it should be, but how map overcomes this?
map has some special syntax, so you can't entirely implement it in pure-perl, but this would come pretty close to it (as long as you're using the block form of map):
sub Map(&#) {
my ($function, #sequence) = #_;
my #result;
foreach my $item (#sequence) {
local $_ = $item;
push #result, $function->($item);
}
return #result
}
use Data::Dumper;
my #sample = qw(1 2 3 4 5);
print Dumper Map { $_ * $_ } #sample;
print Dumper map { $_ * $_ } #sample;
$_ being undefined is overcome by using local $_ instead of my $_. Actually you almost never want to use my $_ (even though you do want to use it on almost all other variables).
Adding the (&#) prototype allows you not to specify sub in front of the block. Again, you almost never want to use prototypes but this is a valid use of them.
While the accepted answer implements a map-like function, it does NOT do it in the way perl would. An important part of for, foreach, map, and grep is that the $_ they provide to you is always an alias to the values in the argument list. This means that calling something like s/a/b/ in any of those constructs will modify the elements they were called with. This allows you to write things like:
my ($x, $y) = qw(foo bar);
$_ .= '!' for $x, $y;
say "$x $y"; # foo! bar!
map {s/$/!!!/} $x, $y;
say "$x $y"; # foo!!!! bar!!!!
Since in your question, you have asked for Map to use array references rather than arrays, here is a version that works on array refs that is as close to the builtin map as you can get in pure Perl.
use 5.010;
use warnings;
use strict;
sub Map (&\#) {
my ($code, $array) = splice #_;
my #return;
push #return, &$code for #$array;
#return
}
my #sample = qw(1 2 3 4 5);
say join ', ' => Map { $_ * $_ } #sample; # 1, 4, 9, 16, 25
say join ', ' => map { $_ * $_ } #sample; # 1, 4, 9, 16, 25
In Map, the (&\#) prototype tells perl that the Map bareword will be parsed with different rules than a usual subroutine. The & indicates that the first argument will either be a bare block Map {...} NEXT or it will be a literal code reference Map \&somesub, NEXT. Note the comma between the arguments in the latter version. The \# prototype indicates that the next argument will start with # and will be passed in as an array reference.
Finally, the splice #_ line empties #_ rather than just copying the values out. This is so that the &$code line will see an empty #_ rather than the args Map received. The reason for &$code is that it is the fastest way to call a subroutine, and is as close to the multicall calling style that map uses as you can get without using C. This calling style is perfectly suited for this usage, since the argument to the block is in $_, which does not require any stack manipulation.
In the code above, I cheat a little bit and let for do the work of localizing $_. This is good for performance, but to see how it works, here is that line rewritten:
for my $i (0 .. $#$array) { # for each index
local *_ = \$$array[$i]; # install alias into $_
push #return, &$code;
}
My Object::Iterate module is an example of what you are trying to do.

Perl, evaluate string lazily

Consider the following Perl code.
#!/usr/bin/perl
use strict;
use warnings;
$b="1";
my $a="${b}";
$b="2";
print $a;
The script obviously outputs 1. I would like it to be whatever the current value of $b is.
What would be the smartest way in Perl to achieve lazy evaluation like this? I would like the ${b} to remain "unreplaced" until $a is needed.
I'm more interested in knowing why you want to do this. You could use a variety of approaches depending on what you really need to do.
You could wrap up the code in a coderef, and only evaluate it when you need it:
use strict; use warnings;
my $b = '1';
my $a = sub { $b };
$b = '2';
print $a->();
A variant of this would be to use a named function as a closure (this is probably the best approach, in the larger context of your calling code):
my $b = '1';
sub print_b
{
print $b;
}
$b = '2';
print_b();
You could use a reference to the original variable, and dereference it as needed:
my $b = '1';
my $a = \$b;
$b = '2';
print $$a;
What you want is not lazy evaluation, but late binding. To get it in Perl, you need to use eval.
my $number = 3;
my $val = "";
my $x = '$val="${number}"';
$number = 42;
eval $x;
print "val is now $val\n";
Be advised that eval is usually inefficient as well as methodically atrocious. You are almost certainly better off using a solution from one of the other answers.
Perl will interpolate a string when the code runs, and i don't know of a way to make it not do so, short of formats (which are ugly IMO). What you could do, though, is change "when the code runs" to something more convenient, by wrapping the string in a sub and calling it when you need the string interpolated...
$b = "1";
my $a = sub { "\$b is $b" };
$b = "2";
print &$a;
Or, you could do some eval magic, but it's a bit more intrusive (you'd need to do some manipulation of the string in order to achieve it).
As others have mentioned, Perl will only evaluate strings as you have written them using eval to invoke the compiler at runtime. You could use references as pointed out in some other answers, but that changes the way the code looks ($$a vs $a). However, this being Perl, there is a way to hide advanced functionality behind a simple variable, by using tie.
{package Lazy;
sub TIESCALAR {bless \$_[1]} # store a reference to $b
sub FETCH {${$_[0]}} # dereference $b
sub STORE {${$_[0]} = $_[1]} # dereference $b and assign to it
sub new {tie $_[1] => $_[0], $_[2]} # syntactic sugar
}
my $b = 1;
Lazy->new( my $a => $b ); # '=>' or ',' but not '='
print "$a\n"; # prints 1
$b = 2;
print "$a\n"; # prints 2
You can lookup the documentation for tie, but in a nutshell, it allows you to define your own implementation of a variable (for scalars, arrays, hashes, or file handles). So this code creates the new variable $a with an implementation that gets or sets the current value of $b (by storing a reference to $b internally). The new method is not strictly needed (the constructor is actually TIESCALAR) but is provided as syntactic sugar to avoid having to use tie directly in the calling code.
(which would be tie my $a, 'Lazy', $b;)
You wish to pretend that $a refers to something that is evaluated when $a is used... You can only do that if $a is not truly a scalar, it could be a function (as cHao's answer) or, in this simple case, a reference to the other variable
my $b="1";
my $a= \$b;
$b="2";
print $$a;
I would like the ${b} to remain "unreplaced" until $a is needed.
Then I'd recommend eschewing string interpolation, instead using sprintf, so that you "interpolate" when needed.
Of course, on this basis you could tie together something quick(ish) and dirty:
use strict;
use warnings;
package LazySprintf;
# oh, yuck
sub TIESCALAR { my $class = shift; bless \#_, $class; }
sub FETCH { my $self = shift; sprintf $self->[0], #$self[1..$#$self]; }
package main;
my $var = "foo";
tie my $lazy, 'LazySprintf', '%s', $var;
print "$lazy\n"; # prints "foo\n"
$var = "bar";
print "$lazy\n"; # prints "bar\n";
Works with more exotic format specifiers, too. Yuck.

Why does my Perl max() function always return the first element of the array?

I am relatively new to Perl and I do not want to use the List::Util max function to find the maximum value of a given array.
When I test the code below, it just returns the first value of the array, not the maximum.
sub max
{
my #array = shift;
my $cur = $array[0];
foreach $i (#array)
{
if($i > $cur)
{
$cur = $i;
}
else
{
$cur = $cur;
}
}
return $cur;
}
Replace
my #array = shift;
with
my #array = #_;
#_ is the array containing all function arguments. shift only grabs the first function argument and removes it from #_. Change that code and it should work correctly!
Why don't you want to use something that works?
One of the ways to solve problems like this is to debug your data structures. At each step you print the data you have to see if what you expect is actually in there. That can be as simple as:
print "array is [#array]\n";
Or for complex data structures:
use Data::Dumper;
print Dumper( \#array );
In this case, you would have seen that #array has only one element, so there it must be the maximum.
If you want to see how list assignment and subroutine arguments work, check out Learning Perl.
You can write the function as:
#!/usr/bin/perl
use strict; use warnings;
print max(#ARGV);
sub max {
my $max = shift;
$max >= $_ or $max = $_ for #_;
return $max;
}
However, it would be far more efficient to pass it a reference to the array and even more efficient to use List::Util::max.

How do I pass a hash to a function in Perl?

I have a function that takes a variable and an associative array, but I can't seem to get them to pass right. I think this has something to do with function declarations, however I can't figure out how they work in Perl. Is there a good reference for this and how do I accomplish what I need?
I should add that it needs to be passed by reference.
sub PrintAA
{
my $test = shift;
my %aa = shift;
print $test . "\n";
foreach (keys %aa)
{
print $_ . " : " . $aa{$_} . "\n";
$aa{$_} = $aa{$_} . "+";
}
}
Pass the reference instead of the hash itself. As in
PrintAA("abc", \%fooHash);
sub PrintAA
{
my $test = shift;
my $aaRef = shift;
print $test, "\n";
foreach (keys %{$aaRef})
{
print $_, " : ", $aaRef->{$_}, "\n";
}
}
See also perlfaq7: How can I pass/return a {Function, FileHandle, Array, Hash, Method, Regex}?
This code works:
#!/bin/perl -w
use strict;
sub PrintAA
{
my($test, %aa) = #_;
print $test . "\n";
foreach (keys %aa)
{
print $_ . " : " . $aa{$_} . "\n";
}
}
my(%hash) = ( 'aaa' => 1, 'bbb' => 'balls', 'ccc' => \&PrintAA );
PrintAA("test", %hash);
The key point is the use of the array context in the my() 'statement' in the function.
What does the array context business actually do?
Succinctly, it makes it work correctly.
It means that the first value in the #_ array of arguments is assigned to $test, and the remaining items are assigned to the hash %aa. Given the way I called it, there is an odd number of items in the #_, so once the first item is assigned to $test, there is an even number of items available to assign to %aa, with the first item of each pair being the key ('aaa', 'bbb', 'ccc' in my example), and the second being the corresponding value.
It would be possible to replace %aa with #aa, in which case, the array would have 6 items in it. It would also be possible to replace %aa with $aa, and in that case, the variable $aa would contain the value 'aaa', and the remaining values in #_ would be ignored by the assignment.
If you omit the parentheses around the variable list, Perl refuses to compile the code.
One of the alternative answers showed the notation:
my $test = shift;
my(%aa) = #_;
This is pretty much equivalent to what I wrote; the difference is that after the two my statements, #_ only contains 6 elements in this variation, whereas in the single my version, it still contains 7 elements.
There are definitely other questions in SO about array context.
Actually, I wasn't asking about the my($test, %aa) = #_; I was asking about my(%hash) = ( 'aaa' => 1, 'bbb' => 'balls', 'ccc' => \&PrintAA ); versus my %hash = { 'aaa' => 1, ... };
The difference is that the { ... } notation generates a hash ref and the ( ... ) notation generates a list, which maps to a hash (as opposed to hash ref). Similarly, [ ... ] generates an array ref and not an array.
Indeed, change the 'main' code so it reads: my(%hash) = { ... }; and you get a run-time (but not compile time) error - treat line numbers with caution since I've added alternative codings to my file:
Reference found where even-sized list expected at xx.pl line 18.
...
Use of uninitialized value in concatenation (.) or string at xx.pl line 13.
Alternatively:
sub PrintAA
{
my $test = shift;
my %aa = #_;
print $test . "\n";
foreach (keys %aa)
{
print $_ . " : " . $aa{$_} . "\n";
$aa{$_} = $aa{$_} . "+";
}
}
The thing you're fundamentally missing is that an associative array isn't a single argument (though an associative array reference is, as in Paul Tomblin's answer).
It looks like you should pass in a reference to a hash.
sub PrintAA
{
my $test = shift;
my $aa = shift;
if (ref($aa) != "HASH") { die "bad arg!" }
....
}
PrintAA($foo, \%bar);
The reason you can't do a
my %aa = shift;
is because Perl flattens all the arguments to a subroutine into one list, #_. Every element is copied, so passing in by reference avoids those copies as well.
As usual there are several ways. Here is what Perl Best Practices, that most revered of style pointers, has to say about passing parameters to functions:
Use a hash of named arguments for any subroutine that has more than three parameters
But since you have only two, you could get away ;) with passing them directly like this:
my $scalar = 5;
my %hash = (a => 1, b => 2, c => 3);
func($scalar, %hash)
And function is defined like this:
sub func {
my $scalar_var = shift;
my %hash_var = #_;
... Do something ...
}
It could be more useful if you could show some code.
All the methods in previous answers work, but this was always the way I preferred to do things like this:
sub PrintAA ($\%)
{
my $test = shift;
my %aa = ${shift()};
print "$test\n";
foreach (keys %aa)
{
print "$_ : $aa{$_}\n";
$aa{$_} = "$aa{$_}+";
}
}
Note: I also changed your code a bit. Perl's double-quoted strings will interpret "$test" to be the value of $test rather than the actual string '$test', so you don't need that many .s.
Also, I was wrong about how the prototypes work. To pass a hash, use this:
PrintAA("test", %hash);
To print a hash reference, use this:
PrintAA("test", %$ref_to_hash);
Of course, now you can't modify the hash referenced by $ref_to_hash because you're sending a copy, but you can modify a raw %hash because you're passing it as a reference.
Arguments to functions get flattened into a single array (#_). So it's usually easiest to pass hashes to a function by reference.
To create a hash:
my %myhash = ( key1 => "val1", key2 => "val2" );
To create a reference to that hash:
my $href = \%myhash
To access that hash by reference;
%$href
So in your sub:
my $myhref = shift;
keys %$myhref;
All the other replies here so far seem rather complicated to me. When I write Perl function I usually "expand" all the passed arguments in the first line of the function.
sub someFunction {
my ( $arg1, $arg2, $arg3 ) = #_;
This is similar to other languages, where you declare functions as
... someFunction ( arg1, arg2, arg3 )
And if you do it that way and pass the hash as the last argument, you'll be fine without any tricks or special magic. E.g.:
sub testFunc {
my ( $string, %hash ) = #_;
print "$string $hash{'abc'} $hash{'efg'} $string\n";
}
my %testHash = (
'abc' => "Hello,",
'efg' => "World!"
);
testFunc('!!!', %testHash);
The output is as expected:
!!! Hello, World! !!!
This works because in Perl arguments are always passed as an array of scalar values and if you pass a hash, its key value/pairs are added to that array. In the sample above, the arguments passed to the function as array (#_) are in fact:
'!!!', 'abc', 'Hello,', 'efg', 'World!'
and '!!!' is simply assigned to %string, while %hash "swallows" all the other arguments, always interpreting one as a key and the next one as value (until all elements are used up).
You cannot pass multiple hashes that way and the hash cannot be the first argument, as otherwise it would swallow everything and leave all other arguments unassigned.
Of course, exactly the same works for array as a last argument. The only difference here is that arrays don't distinguish between keys and values. For them, all arguments left over are values and just get pushed to the array.
Use the following sub to get the hash or hashref - whatever is passed :)
sub get_args { ref( $_[0] ) ? shift() : ( #_ % 2 ) ? {} : {#_}; }
sub PrintAA
{
my $test = shift;
my $aa = get_args(#_);;
# Then
$aa->{somearg} # Do something
$aa->{anotherearg} # Do something
}
Call your function like this:
printAA($firstarg,somearg=>1, anotherarg=>2)
Or like this (no matter):
printAA($firstarg, {somearg=>1, anotherarg=>2})
Or even like this (no matter):
my(%hash) = ( 'aaa' => 1, 'bbb' => 'balls', 'ccc' => \PrintAA );
PrintAA("test", %hash);