Why am I not working in Perl?

I've never used Perl, but I need to complete this exercise. My task is to sort the array in several ways. I was given a test script. This script concatenates the array and outputs expressions for each step of sorting it. I 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 the MyIxHash package that I created for the MyIxHash.pm module. The script goes through an alphabetical sort: "SortByKey", which I inherited from the "IxHash" package in my module. The last two kinds are the ones that give me problems. When the created sub: "SortKeyByFunc" is run on an array, it passes to the array and the sub as arguments. I tried to take these arguments and bind them to variables.

The final sort must be sorted by string length and then alphabetically. The subroutine for this is listed at the bottom of foo.pl as "abcByLength". Just like the reverse alpha sort, this routine is passed as a parameter to my SortKeyByFunc routine.

For both of these types, it seems that the actual sort is done for me, and I just need to apply this routine to my array.

My main problem here is that I don't know how, if possible, to take an argument of a subroutine and run my array through it as a parameter. Am I using my method incorrectly on my array?

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;

      

+3


source to share


2 answers


"My problem comes up when I try: my @sorted = @letters->$reverse();

I also tried: my @sorted = sort {$reverse} @letters;

"

You were very close; correct syntax:

my $reverse = sub { $b cmp $a };
# ...
my @sorted = sort $reverse @letters;

      

Also note that, since mostly for historical reasons, sort

passes arguments to the comparison function in (slightly) magic globals $a

and $b

and not in @_

, so you are not doing the necessary (and should not) do my ($a, $b) = @_;

in your sortsubs (unless you declare them to be prototyped. see perldoc -f sort for details).


Edit: If you're given a comparison function that expects its arguments in for some reason @_

, 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 problem /. When you write:

my @letters = $_[0][1];

      

what you end up with a is a singleton array containing everything $_[0][1]

that is supposed to be a reference . You must either dereference it immediately, for example:

my @letters = @{ $_[0][1] };

      



or just keep this as a link for the moment and play it out when you use it:

my $letters = $_[0][1];
# ...
my @sorted = sort $whatever @$letters;

      


Edit 3: Once you manage to sort the keys, then, as duskwuff points out in his original answer, you will also need to call a method Reorder()

from your parent class, Tie :: IxHash , to actually reverse the order of the keys. Also, the first line:

my ($a, $b) = @_;

      

completely out of place in what should be an object method that accepts a code reference (and, in fact, lexicalization $a

and $b

is a bad idea if you want to call sort

later in the same code block). What he should read is something like:

my ($self, $sortfunc) = @_;

      

In fact, instead of listing everything that seems wrong with your original code, it would 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. Now I see why the comparison functions were specified as taking their arguments in @_

rather than globals $a

and $b

where they sort

usually put them: this is because the comparison functions belong to a different package $a

and are $b

not magical enough to be the same in every package, for example, $_

and @_

. I guess this could be bypassed, but it would take some pretty nontrivial trickery with caller

.)

(Pps. Please do me credit and duskwuff / Stack Overflow as you do your exercise. And good luck learning Perl - trust me, it will be a useful skill.)

+5


source


Your method SortKeyByFunc

returns the results of sorting the array ( @sorted

), but it does not modify the array in place. As a result, just calling $t->SortKeyByFunc(...);

does not produce visible permanent effects.

You will need to call $t->Reorder()

in your method SortKeyByFunc

to have any long term effect on the array. I haven't tried it, but something like:



$t->Reorder(@sorted);

      

at the end of your method might suffice.

+4


source







All Articles