[32808] in Perl-Users-Digest
Perl-Users Digest, Issue: 4072 Volume: 11
daemon@ATHENA.MIT.EDU (Perl-Users Digest)
Thu Nov 7 00:09:37 2013
Date: Wed, 6 Nov 2013 21:09:04 -0800 (PST)
From: Perl-Users Digest <Perl-Users-Request@ruby.OCE.ORST.EDU>
To: Perl-Users@ruby.OCE.ORST.EDU (Perl-Users Digest)
Perl-Users Digest Wed, 6 Nov 2013 Volume: 11 Number: 4072
Today's topics:
Re: Dumper <jl_post@hotmail.com>
How can I pick a module depending if I have it or not? <jl_post@hotmail.com>
Re: How can I pick a module depending if I have it or n <bill@todbe.com>
Re: How can I pick a module depending if I have it or n <rweikusat@mobileactivedefense.com>
Re: How can I pick a module depending if I have it or n <rweikusat@mobileactivedefense.com>
Re: How can I pick a module depending if I have it or n <derykus@gmail.com>
Re: How can I pick a module depending if I have it or n <ben@morrow.me.uk>
Re: readdir <jl_post@hotmail.com>
Re: Several Perl Questions - Nov. 5, 2013 <ben@morrow.me.uk>
Re: Several Perl Questions - Nov. 5, 2013 <ben@morrow.me.uk>
Re: Several Perl Questions - Nov. 5, 2013 <justin.1303@purestblue.com>
sorting by prior value in a deeply nested hash <cartercc@gmail.com>
Re: sorting by prior value in a deeply nested hash <jimsgibson@gmail.com>
Digest Administrivia (Last modified: 6 Apr 01) (Perl-Users-Digest Admin)
----------------------------------------------------------------------
Date: Wed, 6 Nov 2013 15:21:33 -0800 (PST)
From: "jl_post@hotmail.com" <jl_post@hotmail.com>
Subject: Re: Dumper
Message-Id: <4bf11bac-8e65-4eea-b06a-a1c10e46d702@googlegroups.com>
On Tuesday, October 15, 2013 6:36:07 AM UTC-6, George Mpouras wrote:
> # this works
>=20
>=20
>=20
>=20
>=20
>=20
>=20
> use Data::Dumper; $Data::Dumper::Purity=3D1;
>=20
> my $dmp;
>=20
> my %hash1 =3D ('k1'=3D>'v1','k2'=3D>'v2');
>=20
> $dmp =3D Data::Dumper::Dumper( \%hash1 );;
>=20
> my $hash2 =3D eval $dmp; print Data::Dumper::Dumper($hash2);
>=20
>=20
>=20
>=20
>=20
> # this is not
>=20
>=20
>=20
> use strict; use warnings;
>=20
> use Data::Dumper; $Data::Dumper::Purity=3D1;
>=20
> my $dmp;
>=20
> my %hash1 =3D ('k1'=3D>'v1','k2'=3D>'v2');
>=20
> $dmp =3D Data::Dumper::Dumper( \%hash1 );;
>=20
> my $hash2 =3D eval $dmp; print Data::Dumper::Dumper($hash2);
>=20
>=20
>=20
>=20
>=20
> what I am doing wrong
Dear George,
By now you've figured out that the $VAR1 variable in the Data::Dumper::D=
umper() output is conflicting with "use strict;", and that setting $Data::D=
umper::Terse to 1 removes that conflict.
However, I question why you're dumping the output to Dumper into a strin=
g and then eval()ing it to assign to another hash. Is it because you want =
a convenient way to create a deep copy of a hash/array/object?
If so, I recommend using the Storable module instead. It's a standard m=
odule in Perl these days. It avoids the use of having to use eval(), and i=
t's probably considerably quicker than doing the same thing with Data::Dump=
er.
Here I'm giving you three different ways to use the Storable module, one=
of which should nicely match what you're trying to do:
{
# Making a copy from a temporary string (using freeze() and thaw()):
use strict;
use warnings;
my %hash1 =3D ('k1' =3D> 'v1', 'k2' =3D> 'v2');
use Storable;
my $tempString =3D Storable::freeze( \%hash1 );
my %hash2 =3D %{ Storable::thaw($tempString) };
use Data::Dumper; print Data::Dumper::Dumper( \%hash2 );
}
{
# Making a copy from a file (using nstore() and retrieve()):
use strict;
use warnings;
my %hash1 =3D ('k1' =3D> 'v1', 'k2' =3D> 'v2');
my $fileName =3D "file.tmp";
use Storable;
Storable::nstore( \%hash1, $fileName );
my %hash2 =3D %{ Storable::retrieve($fileName) };
use Data::Dumper; print Data::Dumper::Dumper( \%hash2 );
}
{
# Making a deep copy directly (using dclone()):
use strict;
use warnings;
my %hash1 =3D ('k1' =3D> 'v1', 'k2' =3D> 'v2');
use Storable;
my %hash2 =3D %{ Storable::dclone( \%hash1 ) };
use Data::Dumper; print Data::Dumper::Dumper( \%hash2 );
}
If all you want to do is to perform a deep copy on a hash/array/object, =
I advise using the third approach I listed (the one that uses Storable::dcl=
one()). But if you want the data to a file to retrieve later, I recommend =
the approach that uses nstore() and retrieve().
Using eval() to copy data is not always a great idea. For one thing, if=
you the data that you eval() comes from a file, someone could have easily =
tampered with the contents of the file, placing malicious Perl code in the =
file that ends up getting executed when you eval() it.
So if all you want is to store data to a file (for later retrieval), use=
Storable::nstore() (and retrieve it later with Storable::retrieve()) inste=
ad of Data::Dumper. Or if all you want is to create a deep copy/clone of a=
n object, use Storable::dclone().
The main reason you'd want to use Data::Dumper is to visually inspect th=
e data you're working with, which is something you can't do easily with the=
Storable module (which is why I'm using Data::Dumper in all three of my ex=
amples above). (But if you only really needed to visually inspect the data=
, there'd be no reason to eval() it later.)
So my advice is to use the Storable module instead of Data::Dumper, unle=
ss you need to manually inspect the contents of a scalar/array/hash/object.
I hope this is helpful, George.
-- Jean-Luc
------------------------------
Date: Wed, 6 Nov 2013 10:27:21 -0800 (PST)
From: "jl_post@hotmail.com" <jl_post@hotmail.com>
Subject: How can I pick a module depending if I have it or not?
Message-Id: <ecf7064d-9290-4550-8c74-c2ab599c6c54@googlegroups.com>
Dear Perl community,
Recently I had to debug a Perl script in a Windows environment that was =
meant for a Unix environment. The script used the module Device::SerialPor=
t, and since I didn't have it on my Windows installation of Strawberry Perl=
, the compiler check "perl -c script.pl" was giving me errors.
No problem, I thought; I'll just use the "cpan Device::SerialPort" to in=
stall it, and that'll be the end of my problem. Well, I wasn't able to ins=
tall the Device::SerialPort module through Strawberry Perl, so I looked aro=
und CPAN and the internets to see what I could do.
I discovered a very similar module named Win32::SerialPort. It is so si=
milar to Device::SerialPort that many of the method names are the same. An=
d I was able to install Win32::SerialPort onto my Strawberry Perl setup. A=
fter that I was able to change:
use Device::SerialPort;
my $port =3D Device::SerialPort->new($portName)
or die "Can't establish connection with $portName.\n";
to:
use Win32::SerialPort;
my $port =3D Win32::SerialPort->new($portName)
or die "Can't establish connection with $portName.\n";
and the rest of the code (that involved the SerialPort, at least) compiled =
just fine.
However, once I finished debugging the script I had to remember to chang=
e the "Win32::SerialPort" module to "Device::SerialPort" before I sent the =
script back for use on a Unix platform. Eventually an idea came to me that=
I could let the script determine which module to use (depending on whether=
that module existed). In other words, if Device::SerialPort exists, use t=
hat one, but if not, use Win32::SerialPort. (And if neither exist, exit/di=
e with an informative error message.)
So my question is: How can I use a module if it is installed, or anothe=
r if it is not installed?
I want to be able to do something like this:
my $port =3D do
{
if (use Device::SerialPort)
{
Device::SerialPort->new($portName)
or die "Can't establish connection with $portName.\n";
}
elsif (use Win32::SerialPort)
{
Win32::SerialPort->new($portName)
or die "Can't establish connection with $portName.\n";
}
else
{
die "This script requires the Device::SerialPort or Win32::SerialP=
ort module.\n";
}
};
Of course this code won't compile, so I was wondering if someone knows h=
ow to get a Perl script to do what I want -- that is, to detect which modul=
es (out of several) exist, and to properly load them as if I used them with=
normal "use" syntax.
Thanks in advance,
-- Jean-Luc
------------------------------
Date: Wed, 06 Nov 2013 11:00:46 -0800
From: "$Bill" <bill@todbe.com>
Subject: Re: How can I pick a module depending if I have it or not?
Message-Id: <l5e3ks$o47$1@dont-email.me>
On 11/6/2013 10:27, jl_post@hotmail.com wrote:
> Dear Perl community,
>
> Recently I had to debug a Perl script in a Windows environment that was meant for a Unix environment. The script used the module Device::SerialPort, and since I didn't have it on my Windows installation of Strawberry Perl, the compiler check "perl -c script.pl" was giving me errors.
>
> No problem, I thought; I'll just use the "cpan Device::SerialPort" to install it, and that'll be the end of my problem. Well, I wasn't able to install the Device::SerialPort module through Strawberry Perl, so I looked around CPAN and the internets to see what I could do.
>
> I discovered a very similar module named Win32::SerialPort. It is so similar to Device::SerialPort that many of the method names are the same. And I was able to install Win32::SerialPort onto my Strawberry Perl setup. After that I was able to change:
>
> use Device::SerialPort;
> my $port = Device::SerialPort->new($portName)
> or die "Can't establish connection with $portName.\n";
>
> to:
>
> use Win32::SerialPort;
> my $port = Win32::SerialPort->new($portName)
> or die "Can't establish connection with $portName.\n";
>
> and the rest of the code (that involved the SerialPort, at least) compiled just fine.
>
> However, once I finished debugging the script I had to remember to change the "Win32::SerialPort" module to "Device::SerialPort" before I sent the script back for use on a Unix platform. Eventually an idea came to me that I could let the script determine which module to use (depending on whether that module existed). In other words, if Device::SerialPort exists, use that one, but if not, use Win32::SerialPort. (And if neither exist, exit/die with an informative error message.)
>
> So my question is: How can I use a module if it is installed, or another if it is not installed?
>
> I want to be able to do something like this:
>
> my $port = do
> {
> if (use Device::SerialPort)
> {
> Device::SerialPort->new($portName)
> or die "Can't establish connection with $portName.\n";
> }
> elsif (use Win32::SerialPort)
> {
> Win32::SerialPort->new($portName)
> or die "Can't establish connection with $portName.\n";
> }
> else
> {
> die "This script requires the Device::SerialPort or Win32::SerialPort module.\n";
> }
> };
>
>
> Of course this code won't compile, so I was wondering if someone knows how to get a Perl script to do what I want -- that is, to detect which modules (out of several) exist, and to properly load them as if I used them with normal "use" syntax.
Try using eval - something like:
eval "use Device::SerialPort";
if ($@) {
print "Can't find Device::SerialPort\n";
eval "use Win32::SerialPort";
if ($@) {
die "Can't find Win32::SerialPort - quitting";
} else {
print "Found Win32::SerialPort - using it\n";
}
} else {
print "Found Device::SerialPort - using it\n";
}
------------------------------
Date: Wed, 06 Nov 2013 20:37:06 +0000
From: Rainer Weikusat <rweikusat@mobileactivedefense.com>
Subject: Re: How can I pick a module depending if I have it or not?
Message-Id: <87mwlhjmh9.fsf@sable.mobileactivedefense.com>
"jl_post@hotmail.com" <jl_post@hotmail.com> writes:
> Recently I had to debug a Perl script in a Windows environment that
> was meant for a Unix environment. The script used the module
> Device::SerialPort, and since I didn't have it on my Windows
> installation of Strawberry Perl, the compiler check "perl -c
> script.pl" was giving me errors.
[...]
> I discovered a very similar module named Win32::SerialPort. It is
> so similar to Device::SerialPort that many of the method names are
> the same.
[...]
> However, once I finished debugging the script I had to remember to
> change the "Win32::SerialPort" module to "Device::SerialPort"
> before I sent the script back for use on a Unix platform.
[...]
> So my question is: How can I use a module if it is installed, or
> another if it is not installed?
Someone else must have implemented this already but you could use this
small module:
-------------
package Delegate;
sub import
{
my ($candidates, $fname);
shift;
$candidates = shift;
for (@$candidates) {
$fname = $_;
$fname =~ s/::/\//g;
$fname .= '.pm';
eval {
require $fname;
};
$@ || do {
unshift(@_, $_);
$_ .= "::import";
goto &$_;
};
}
die("No way man ... \n");
}
1;
---------------
Example usage
---------------
use Delegate (['B::Mad', 'ModA'], 'for_whats_it_worth');
print(for_whats_it_worth(), "\n");
---------------
The first argument is a reference to an anonymous array containing a
list of candiate module names, the others are an ordinary import list.
NB: This doesn't duplicate the documented functionality of use ... /
require Bareword completely as it doesn't try loading a file with .pmc
at the end.
------------------------------
Date: Wed, 06 Nov 2013 22:42:00 +0000
From: Rainer Weikusat <rweikusat@mobileactivedefense.com>
Subject: Re: How can I pick a module depending if I have it or not?
Message-Id: <87fvr9jgp3.fsf@sable.mobileactivedefense.com>
Rainer Weikusat <rweikusat@mobileactivedefense.com> writes:
> "jl_post@hotmail.com" <jl_post@hotmail.com> writes:
>> Recently I had to debug a Perl script in a Windows environment that
>> was meant for a Unix environment. The script used the module
>> Device::SerialPort, and since I didn't have it on my Windows
>> installation of Strawberry Perl, the compiler check "perl -c
>> script.pl" was giving me errors.
>
> [...]
>
>> I discovered a very similar module named Win32::SerialPort. It is
>> so similar to Device::SerialPort that many of the method names are
>> the same.
>
> [...]
>
>> However, once I finished debugging the script I had to remember to
>> change the "Win32::SerialPort" module to "Device::SerialPort"
>> before I sent the script back for use on a Unix platform.
>
> [...]
>
>> So my question is: How can I use a module if it is installed, or
>> another if it is not installed?
>
> Someone else must have implemented this already but you could use this
> small module:
>
> -------------
> package Delegate;
>
> sub import
> {
> my ($candidates, $fname);
>
> shift;
> $candidates = shift;
>
> for (@$candidates) {
> $fname = $_;
> $fname =~ s/::/\//g;
> $fname .= '.pm';
>
> eval {
> require $fname;
> };
>
> $@ || do {
> unshift(@_, $_);
> $_ .= "::import";
> goto &$_;
> };
> }
>
> die("No way man ... \n");
> }
>
> 1;
> ---------------
While this is a neat idea, it won't work with modules which don't define
or inherit an import method, won't work with OO-modules at all and is
restricted to a single set of delegations per program. Something more
suitable (for the problem situation which was about OO-modules) could
look like this:
-------------
sub require_one
{
my $fname;
for (@_) {
$fname = $_;
$fname =~ s/::/\//g;
$fname .= '.pm';
eval {
require $fname;
};
return $_ unless $@;
}
die("need any of ".join(', ', @_)."\n");
}
-------------
This expects a list of module names as arguments and will return the
name of the first one which could be loaded. This name can then used to
call class methods like this
$serial_class = require_one('Win32::SerialPort', 'Device::SerialPort');
$port = $serial_class->new($portName);
------------------------------
Date: Wed, 06 Nov 2013 17:09:26 -0800
From: Charles DeRykus <derykus@gmail.com>
Subject: Re: How can I pick a module depending if I have it or not?
Message-Id: <l5ep8m$f0g$1@speranza.aioe.org>
On 11/6/2013 10:27 AM, jl_post@hotmail.com wrote:
> ...
> So my question is: How can I use a module if it is installed, or another if it is not installed?
>
> I want to be able to do something like this:
>
> my $port = do
> {
> if (use Device::SerialPort)
> {
> Device::SerialPort->new($portName)
> or die "Can't establish connection with $portName.\n";
> }
> elsif (use Win32::SerialPort)
> {
> Win32::SerialPort->new($portName)
> or die "Can't establish connection with $portName.\n";
> }
> else
> {
> die "This script requires the Device::SerialPort or Win32::SerialPort module.\n";
> }
> };
>
Maybe a combination of 'use if' and Module::Load::Conditional ...
use Module::Load::Condition qw/check_install/;
our( $m1, $m2 );
use if $m1=check_install(module=>Device::SerialPort),Device::SerialPort;
use if $m2=check_install(module=>Win32::SerialPort), Win32::SerialPort;
die "This script requires..." unless $m1 or $m2;
--
Charles DeRykus
------------------------------
Date: Thu, 7 Nov 2013 02:44:18 +0000
From: Ben Morrow <ben@morrow.me.uk>
Subject: Re: How can I pick a module depending if I have it or not?
Message-Id: <2dbrka-m9b1.ln1@anubis.morrow.me.uk>
Quoth "jl_post@hotmail.com" <jl_post@hotmail.com>:
>
> So my question is: How can I use a module if it is installed, or
> another if it is not installed?
>
> I want to be able to do something like this:
>
> my $port = do
> {
> if (use Device::SerialPort)
Since these are OO modules, so you don't need exports, just use require:
if (eval "require Device::SerialPort; 1") {
(the "; 1" works around a rather obscure bug in some versions of 5.8;
it's probably obsolete at this point but I still put it in out of
habit.)
> {
> Device::SerialPort->new($portName)
> or die "Can't establish connection with $portName.\n";
> }
> elsif (use Win32::SerialPort)
> {
> Win32::SerialPort->new($portName)
> or die "Can't establish connection with $portName.\n";
> }
> else
> {
> die "This script requires the Device::SerialPort or
> Win32::SerialPort module.\n";
> }
> };
Of course, I would refactor this to
my $port;
for my $M (qw/Device::SerialPort Win32::SerialPort/) {
if (eval "require $M; 1") {
$port = $M->new($portName);
last;
}
}
$port or die "No serial port module found.\n";
or possibly
use List::Util qw/first/;
my $SerialPort = first { eval "require $_; 1" }
qw/Device::SerialPort Win32::SerialPort/
or die "No serial port module found.\n";
my $port = $SerialPort->new($portName);
If you find you need imports, just spell out the 'use' explicitly:
use List::Util qw/first/;
my $SerialPort;
BEGIN {
$SerialPort = first ... # as before
$SerialPort->import(...);
}
my $port = $SerialPort->new(...);
Ben
------------------------------
Date: Wed, 6 Nov 2013 10:48:48 -0800 (PST)
From: "jl_post@hotmail.com" <jl_post@hotmail.com>
Subject: Re: readdir
Message-Id: <373ebfe0-0b9b-41e6-912b-009e17564d65@googlegroups.com>
On Tuesday, October 29, 2013 4:06:59 PM UTC-6, George Mpouras wrote:
>
> Is there any way readdir to return me files by modification time ?
> I do not want keep their dates on an array and sort it .
> I want one pass like=20
>=20
> ls -ltr
Dear George,
The readdir() function returns files in whatever order it wants. Well, =
there's more to it than that, but you need to remember that you can't assum=
e it will ever be in any meaningful order. That's why Perl gives us the so=
rt() function.
You probably already know that if you want to sort filenames ASCII-betic=
ally, you can do so this way:
@fileNames =3D sort @fileNames;
Sorting by modification time is a bit more involved. Fortunately, you c=
an use a Schwartzian Transform like this one:
@fileNames =3D map {$_->[0]}
sort {$b->[1] <=3D> $a->[1]}
map {[$_, -M $_]} @fileNames;
(It is important to remember that the entries in @fileNames must contain a =
proper path to each file. If the entries are just basenames (that is, they=
have no path) and the files are not in the current working directory, then=
this sort won't work properly.)
It's just one line long (though I had to break it up to avoid line-wrapping=
). If it's not clear to you what it's doing, I suggest you read up on Schw=
artzian Transforms. If you want to stick to the perldocs, you can read abo=
ut them in perlfaq4 with the command:
perldoc -q "sort an array"
Schwartzian Transforms can be very helpful, especially if you want to so=
rt an array of filenames in just one pass. But they're not immediately int=
uitive, which is why it would be wise to study them enough to understand ho=
w to implement them in any programming language.
I hope this helps, George.
-- Jean-Luc
------------------------------
Date: Tue, 5 Nov 2013 22:05:43 +0000
From: Ben Morrow <ben@morrow.me.uk>
Subject: Re: Several Perl Questions - Nov. 5, 2013
Message-Id: <nm6oka-7ni.ln1@anubis.morrow.me.uk>
Quoth "Peter J. Holzer" <hjp-usenet3@hjp.at>:
> On 2013-11-05 15:05, Ben Morrow <ben@morrow.me.uk> wrote:
> >
> > That's not actually true, though it is true that 32bit Windows won't run
> > 64bit executables. 16bit Windows used to run 32bit executables just
> > fine, and Mac OS X for quite a long time had a 32bit kernel that was
> > capable of running 64bit user processes.
>
> There is always the question what "16 bit", "32 bit" and "64 bit" means.
> On some architectures, there were just additional instructions to access
> the longer registers. On others (like x86) there is no binary
> compatibility: You could compile a program to access EAX in 16-bit mode
> or in 32-bit mode, but the machine code would be different, and I think
> the same is true for 32/64bit x86 code.
That's not quite right: %eax is always a 32bit register, the 16bit
version is called %ax and refers to the lower half of %eax. Similarly,
%rax is a 64bit register which includes %eax as its lower half.
> On MS-DOS that mattered little
> because there was no hardware protection anyway, so any executable could
> just switch to 32-bit protected mode (and back into real mode[1] before
> exit), but any real OS (starting with Xenix-286 or protected mode
> Windows) needed to be able to set up 32-bit segments to run "real" 32
> bit executables, but without that it could of course run programs which
> just accessed the 32 bit integer registers (but only for computation,
> not as pointers). I'd hesitate to call Win95 with the Win32 subsystem a
> "16 bit OS": While large parts of it were (AIUI) still running in 16 bit
> protected mode, some parts (especially those dealing with 32 bit
> processes) were running in 32 bit mode (the segmented x86 architecture
> made stuff like that almost natural).
I was talking specifically about Win3.1, which would happily run 32bit
processes (presumably they included the appropriate setup code to switch
to 32bit mode). A notable example was Microsoft nmake, which in the
version of MSVC I had was a 32bit executable.
> I don't know about MacOS, but I
> guess you are talking about x86 here, too (AFAIK, Apple never sold
> machines with 64 bit POWER chips).
10.4 supported 64bit POSIX apps over a 32bit kernel, on amd64 or ppc
(the G5 is 64bit). 10.5 also supported 64bit GUI apps, also amd64 or
ppc. 10.6 was the first version to include a 64bit kernel, amd64 only
(10.6 was the release that dropped support for ppc), but did not always
run it by default. 10.8 was the first release not to include a 32bit
kernel at all.
Ben
------------------------------
Date: Tue, 5 Nov 2013 22:13:47 +0000
From: Ben Morrow <ben@morrow.me.uk>
Subject: Re: Several Perl Questions - Nov. 5, 2013
Message-Id: <r57oka-7ni.ln1@anubis.morrow.me.uk>
Quoth "Peter J. Holzer" <hjp-usenet3@hjp.at>:
> On 2013-11-05 17:42, Ben Morrow <ben@morrow.me.uk> wrote:
> >
> > This is not true. Perl's core numerical ops are written to preserve
> > accuracy rather than for speed,
>
> Which is almost certainly irrelevant. The numerical op itself is a
> single CPU (FPU) instruction and that's always done in extended
> precision on x86. (Unless the compiler uses SSE instructions or
> something similar, but AIUI that's only worthwhile if you are using many
> instructions).
Perl's op_add does a lot more than a machine add. When adding integers
it attempts to preserve integer precision beyond the range of a machine
IV, by upgrading the result first to UV and then to NV if that gives a
larger range with integer precision (which it does on 32bit x86, for
instance).
> > Math::Trig (and Math::Complex) are pure-Perl OO-based
> > implementations; I would expect these to be even slower.
>
> What's object oriented about Math::Trig? That seems to me a rather
> simple collection of convenience functions.
The functions actually defined in Math/Trig.pm all call functions
imported from Math::Complex, which mostly start by converting numbers
into overloaded Math::Complex objects.
Ben
------------------------------
Date: Wed, 6 Nov 2013 11:59:44 +0000
From: Justin C <justin.1303@purestblue.com>
Subject: Re: Several Perl Questions - Nov. 5, 2013
Message-Id: <ginpka-k9g.ln1@zem.masonsmusic.co.uk>
On 2013-11-05, Henry Law <news@lawshouse.org> wrote:
>
> (This has to be by far the worst Perl code I've ever seen in my life).
Do you want to see some of mine? I'm sure I can find something quite
horrific.
Justin.
--
Justin C, by the sea.
------------------------------
Date: Wed, 6 Nov 2013 15:41:46 -0800 (PST)
From: ccc31807 <cartercc@gmail.com>
Subject: sorting by prior value in a deeply nested hash
Message-Id: <783fec5c-89cd-4a51-9cde-ae48642afa3e@googlegroups.com>
I have a reference to a hash defined like this:
$chpr->{$college}{$dept}{$rank}{$fac_id}{$subj} = {
...,
sort_name = $sort_name,
...};
I print an Excel report like this:
#open Excel workbook for COLLEGE
foreach my $college (sort keys %{$chpr}) {
foreach my $dept (sort keys %{$chpr->{$college}}) {
foreach my $rank (sort keys %{$chpr->{$college}{$dept}}) {
#open new worksheet for DEPARTMENT
foreach my $fac_id (sort keys %{$chpr->{$college}{$dept}{$rank}}) {
foreach my $subj (sort keys %{$chpr->{$college}{$dept}{$rank}{$fac_id}}) {
#$worksheet->write_row($row, $col, $arrayref);
#here, I would like to alphabetically sort by
#$chpr->{$college}{$dept}{$rank}{$fac_id}{$subj}{sort_name}
#so that the faculty names appear in alphabetical order listed
#by college, department, rank, and subject (faculty ID isn't wanted)
}
}
}
}
}
I can't see the $subj from $fac_id, and once I get to $subj, the has is already sorted by $fac_id. I can't figure out a way to retroactively unsort by $fac_id and sort by ...{$fac_id}...{sort_name}.
Ordering the faculty by sort_name isn't possible because sort_name is not unique, but fac_id is unique.
Suggestions? Other than rewriting the whole thing?
Thanks, CC.
------------------------------
Date: Wed, 06 Nov 2013 17:16:31 -0800
From: Jim Gibson <jimsgibson@gmail.com>
Subject: Re: sorting by prior value in a deeply nested hash
Message-Id: <061120131716313438%jimsgibson@gmail.com>
In article <783fec5c-89cd-4a51-9cde-ae48642afa3e@googlegroups.com>,
ccc31807 <cartercc@gmail.com> wrote:
> I have a reference to a hash defined like this:
> $chpr->{$college}{$dept}{$rank}{$fac_id}{$subj} = {
> ...,
> sort_name = $sort_name,
> ...};
>
> I print an Excel report like this:
> #open Excel workbook for COLLEGE
> foreach my $college (sort keys %{$chpr}) {
> foreach my $dept (sort keys %{$chpr->{$college}}) {
> foreach my $rank (sort keys %{$chpr->{$college}{$dept}}) {
> #open new worksheet for DEPARTMENT
> foreach my $fac_id (sort keys %{$chpr->{$college}{$dept}{$rank}}) {
> foreach my $subj (sort keys %{$chpr->{$college}{$dept}{$rank}{$fac_id}}) {
> #$worksheet->write_row($row, $col, $arrayref);
> #here, I would like to alphabetically sort by
> #$chpr->{$college}{$dept}{$rank}{$fac_id}{$subj}{sort_name}
> #so that the faculty names appear in alphabetical order listed
> #by college, department, rank, and subject (faculty ID isn't wanted)
> }
> }
> }
> }
> }
>
> I can't see the $subj from $fac_id, and once I get to $subj, the has is
> already sorted by $fac_id. I can't figure out a way to retroactively unsort
> by $fac_id and sort by ...{$fac_id}...{sort_name}.
>
> Ordering the faculty by sort_name isn't possible because sort_name is not
> unique, but fac_id is unique.
>
> Suggestions? Other than rewriting the whole thing?
I would stop at the $rank iteration and create a temporary data
structure that holds the data you want to print for that combination of
($college,$dept,$rank). I think you want to basically invert the hash
array for that point onward.
If you have a hash called %data, then iterate over $fac_id and $subj,
saving the data as $data{$subj}{$fac_id}. Then, when you are done, you
can iterate over subjects, iterate over faculty name, and print.
I might also create a %faculty_names hash using $faculty_name{$fac_id}
= sort_name for looking up the faculty name given the ID.
I would also recommend saving each hash reference in a scalar variable,
thereby getting rid of the multi-level hash fetches:
foreach my $college (sort keys %{$chpr}) {
my $college_ref = $chpr->{$college};
foreach my $dept (sort keys %{$college_ref} ) {
my $dept_ref = $college_ref->{$dept};
...
--
Jim Gibson
------------------------------
Date: 6 Apr 2001 21:33:47 GMT (Last modified)
From: Perl-Users-Request@ruby.oce.orst.edu (Perl-Users-Digest Admin)
Subject: Digest Administrivia (Last modified: 6 Apr 01)
Message-Id: <null>
Administrivia:
To submit articles to comp.lang.perl.announce, send your article to
clpa@perl.com.
Back issues are available via anonymous ftp from
ftp://cil-www.oce.orst.edu/pub/perl/old-digests.
#For other requests pertaining to the digest, send mail to
#perl-users-request@ruby.oce.orst.edu. Do not waste your time or mine
#sending perl questions to the -request address, I don't have time to
#answer them even if I did know the answer.
------------------------------
End of Perl-Users Digest V11 Issue 4072
***************************************