Some sort questions - especially hashes

D

Dave Saville

This started out as one question but in trying to solve my problem I
got side tracked - as you do :)

In the "Perl Cookbook" page 140 under sorting lists there is the
following code:

@ordered = sort { $a->name cmp $b->name } @employees;

Where has 'name' come from? and what is in @employees such that
$a->name means something? I can't work it backwards as it were to
figure it out.

Now the actual problem.

I have a script that is building an HTML table. The table is built
from a hash. The top level entries of the hash are anonymous arrays
the elements of which are the columns.

At present there is a chunk of duplicated formatting code depending on
which of two columns I sort over. Yesterday the inevitable happened in
that I changed the formatting of one set and forgot to make the same
change in the other. The "curse of the duplicated code" :)

So it got me wondering if I could code one formatting loop and
dynamically set the sort up or down.

$foo = \&sortup;
$foo = \&sortdown if $bar;

foreach (sort $foo keys ......

But I recall scope problems in the past using suborutines that seem
solved if one used sort { ..... } syntax

At present one of the sorts looks like:

foreach my $url (sort {$$urlsref{$site}{$b}[0] <=>
$$urlsref{$site}{$a}[0] or $a cmp $b} keys %{$$urlsref{$site}} ) #
count descending

TIA
 
R

Rainer Weikusat

[...]

I have a script that is building an HTML table. The table is built
from a hash. The top level entries of the hash are anonymous arrays
the elements of which are the columns.

At present there is a chunk of duplicated formatting code depending on
which of two columns I sort over. Yesterday the inevitable happened in
that I changed the formatting of one set and forgot to make the same
change in the other. The "curse of the duplicated code" :)

So it got me wondering if I could code one formatting loop and
dynamically set the sort up or down.

$foo = \&sortup;
$foo = \&sortdown if $bar;

foreach (sort $foo keys ......

But I recall scope problems in the past using suborutines that seem
solved if one used sort { ..... } syntax

----------
sub asc
{
return $a <=> $b;
}

sub desc
{
return $b <=> $a;
}

my @in = map { int(rand(100)); } 0 .. 9;
my $cmp;

$cmp = \&asc;
print($_, "\n") for sort $cmp @in;

print("\n");

$cmp = \&desc;
print($_, "\n") for sort $cmp @in;
 
D

Dave Saville

[...]

I have a script that is building an HTML table. The table is built
from a hash. The top level entries of the hash are anonymous arrays
the elements of which are the columns.

At present there is a chunk of duplicated formatting code depending on
which of two columns I sort over. Yesterday the inevitable happened in
that I changed the formatting of one set and forgot to make the same
change in the other. The "curse of the duplicated code" :)

So it got me wondering if I could code one formatting loop and
dynamically set the sort up or down.

$foo = \&sortup;
$foo = \&sortdown if $bar;

foreach (sort $foo keys ......

But I recall scope problems in the past using suborutines that seem
solved if one used sort { ..... } syntax

----------
sub asc
{
return $a <=> $b;
}

sub desc
{
return $b <=> $a;
}

my @in = map { int(rand(100)); } 0 .. 9;
my $cmp;

$cmp = \&asc;
print($_, "\n") for sort $cmp @in;

print("\n");

$cmp = \&desc;
print($_, "\n") for sort $cmp @in;

Thanks.

IIRC I was several depths down in subroutines and using a "sort sub"
syntax to sort and it was similar to this problem with a complicated
deep hash to sort on and it was not sorting correctly until someone on
here pointed out that some part of it was out of scope. It was a long
time ago. But it *may* have been like this:

use .....


foo(..........

exit

sub foo{
.... sort bar .....
}

sub bar {
}

IOW foo and bar are on the same "level" and possibly bar should have
been defined inside foo.
 
D

Dave Saville

From the perldoc: "where the elements to be compared are passed into
the subroutine as the package global variables $a and $b".

So in that block (within the braces) $a and $b represent the two sides
of every comparison that the sort will make; conceptually at any rate
the sort invokes your block, passing $a and $b to it, and makes its sort
decision on the final value.

Yes I understand that part.
I don't know what @employees consists of either, but whatever it is the
construct "$element_of_employees->name" must have some value. An array
of objects which all implement a "name" method would do.

Ah, I thought it might be some sneaky way of sorting on hash keys
assuming the array contained refs to hashes.
 
R

Rainer Weikusat

Ben Morrow said:
You can get problems if the sort call and the sub you are sorting by are
in different packages.

Actually, one will get problems in this case as sort uses the
package-variables $a and $b to pass the objects-to-be-compared into the
comparison routine and if that's not in the same package as the sort call,
naively written code will use a different $a and $a (namely, those of
the package the subroutine belongs to). Symbolic references could be
used to work around that (possibly together with no strict 'refs'):

-------
package AlienCompare;

sub desc {
return ${caller().'::b'} <=> ${caller().'::a'};
}

package Orkshire;

my @in = map { int(rand(100)) } 0 .. 9;

print($_, "\n") for sort AlienCompare::desc @in;
--------


[...]
Named subs should never be defined inside other named subs in Perl. It
doesn't achieve anything useful

Depending on the situation, it might: PostgreSQL functions written in
Perl end up being compiled into anonymous subroutines. Insofar things
get complicated enough that more than one Perl subroutine is needed, the
additional ones have to be defined inside the/ a anonymous subroutine
created by the database server (it is possible to escape from the
anonymous routine by terminating it with a top-level }, add some 'free
code' and then get back into a different anonymous sub at the end of the
Postgres function definition, as you suggested a while ago, but that's a
trick I'd rather avoid).
and sometimes has confusing side-effects
(you will get a 'variable will not stay shared' warning, and the sub
won't see the lexicals you necessarily expect).

IMHO, the warning itself is such a confusing side effect:

----------
use warnings;

sub soup
{
my @components = qw(carrots leeks celeriac garlic onion);

sub components
{
return @components;
}

sub mash_it
{
@components = 'vegetable mash';
}
}

soup();

print('A soup made of ', join(', ', components()), ".\n");

mash_it();

print('Now, we just have ', components(), ".\n");
-----------

Since the two inner subroutine are created 'statically' at
compile-time, I wouldn't expect them to pick up new incarnations of
@components created at run time but they will continue to share the
initial one.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top