|
Posted by harryfmudd [AT] comcast [DOT] on December 5, 2006, 11:49 am
Please log in for more thread options
Mumia W. (reading news) wrote:
> I wanted to see how Sort::Maker could be used to sort the "meascodes" in
> this message:
>
>
http://groups.google.com/groups?selm=1165272390.675288.234670@79g2000cws.googlegroups.com
>
>
> To do this, I wrote this program:
>
> 132 use strict;
> 133 use warnings;
> 134 use Data::Dumper;
> 135 use Sort::Maker;
> 136
> 137 my $cost_order = 'A2yB';
> 138 my @sorted;
> 139
> 140 my $ms = [
> 141 '<table><tr><td meascode=\'y\'> </td></tr></table>',
> 142 '<table><tr><td meascode=\'B\'> </td></tr></table>',
> 143 '<table><tr><td meascode=\'2\'> </td></tr></table>',
> 144 '<table><tr><td meascode=\'A\'> </td></tr></table>'
> 145 ];
> 146
> 147 my $sorter = make_sorter(
> 148 plain => string => sub {
> 149 if (/meascode='(.)'/)
> 150 },
> 151 );
> 152 if ($@) { print "$@\n"; exit; }
> 153 @sorted = $sorter->(@$ms);
> 154 print Dumper(\@sorted);
>
> However, this fails with "Global symbol "$cost_order" requires explicit
> package name at (eval 12) line 8."
>
> I was a little surprised, since anonymous subs usually have access to
> lexical variables. I decided to make $cost_order a package variable (in
> main) and fully qualify it as $::cost_order in the anonymous sub, so I
> changed the lines of the program like so:
>
> 137 our $cost_order = 'A2yB';
> 149 if (/meascode='(.)'/)
>
> However, it fails again with the same error message as before.
> Thankfully, it prints out the key extraction code which shows that my
> colons have been removed from $::cost_order, and even when I use
> $main::cost_order, it gets changed to $cost_order--which causes the
> failure.
>
> I then decided to change the variable $cost_order to a subroutine:
>
> 137 sub COST_ORDER { 'A2yB' }
> 149 if (/meascode='(.)'/)
>
> And I get this error message: "Undefined subroutine
> &Sort::Maker::COST_ORDER called at (eval 12) line 8."
>
> It looks like my anonymous sub is being compiled in Sort::Maker. No
> problem, I'll just put the qualifiers on the COST_ORDER():
>
> 149 if (/meascode='(.)'/)
>
> But it fails with the same error message. I can only assume that the
> colons were stripped from COST_ORDER() before the anonymous sub was
> compiled. Finally, I had an insight: Sort::Maker likes stuff to be in
> its package, so I try this:
>
> 137 sub COST_ORDER { 'A2yB' }
> 146 * = \&COST_ORDER;
> 149 if (/meascode='(.)'/)
>
> And it works.
>
> Evidently, there is a bug/undocumented feature that causes an anonymous
> sub that the programmer writes in his package to be compiled in
> Sort::Maker, and Sort::Maker seems to make some modifications to the
> *text* of that subroutine before it's compiled :O
>
> Perl 5.8.4
> Sort::Maker 0.05
> Debian 3.1 (i686)
>
>
Well, it's quasi-documented. The third paragraph in "Key Extraction
Code" about 60% of the way down the POD says "If a CODE reference is
used, then the B::Deparse module is used to deparse it back into Perl
source. This source is then used to extract the key in the generated
sorter. As with qr//, the advantage is that the extraction code is
syntax checked at compile time and not runtime. Also the deparsed code
is wrapped in a do{} block so you may use complex code to extract the key."
So it's not, strictly speaking, undocumented, but I admit that I find it
an un-Perlish behaviour. His "the advantage is that the extraction code
is syntax checked at compile time and not runtime" seems a bit
irrelevant; if you have a code reference to pass in the first place, it
seems to me that we already know it compiles.
It would seem to me that the author could have done something like
returning the code reference as-is, and made his sort routine call that.
But then, I'm not writing the code.
Tom Wyant
|