Re: Sort::Maker : anonymous sub is compiled outside of my module



"MW(n" == Mumia W (reading news) <paduille.4060.mumia.w@xxxxxxxxxxxxx> writes:

MW(n> 137 my $cost_order = 'A2yB';
MW(n> 138 my @sorted;
MW(n> 139
MW(n> 140 my $ms = [
MW(n> 141 '<table><tr><td meascode=\'y\'> </td></tr></table>',
MW(n> 142 '<table><tr><td meascode=\'B\'> </td></tr></table>',
MW(n> 143 '<table><tr><td meascode=\'2\'> </td></tr></table>',
MW(n> 144 '<table><tr><td meascode=\'A\'> </td></tr></table>'
MW(n> 145 ];
MW(n> 146
MW(n> 147 my $sorter = make_sorter(
MW(n> 148 plain => string => sub {
MW(n> 149 if (/meascode='(.)'/) {index($cost_order,$1)}
MW(n> 150 },
MW(n> 151 );
MW(n> 152 if ($@) { print "$@\n"; exit; }
MW(n> 153 @sorted = $sorter->(@$ms);
MW(n> 154 print Dumper(\@sorted);

MW(n> I was a little surprised, since anonymous subs usually have access to
MW(n> lexical variables. I decided to make $cost_order a package variable
MW(n> (in main) and fully qualify it as $::cost_order in the anonymous sub,
MW(n> so I changed the lines of the program like so:

sort::maker builds a sort sub source and evals it. so that is done in
the Sort::Maker namespace as you learned later on. there is no way for
that code to see any lexicals or subs outside its space.

MW(n> 137 sub COST_ORDER { 'A2yB' }
MW(n> 146 *{Sort::Maker::COST_ORDER} = \&COST_ORDER;
MW(n> 149 if (/meascode='(.)'/) {index(COST_ORDER(),$1)}

MW(n> And it works.

MW(n> Evidently, there is a bug/undocumented feature that causes an
MW(n> anonymous sub that the programmer writes in his package to be compiled
MW(n> in Sort::Maker, and Sort::Maker seems to make some modifications to
MW(n> the *text* of that subroutine before it's compiled :O

there is a feature you missed which can help here. the 'init_code'
option allows you to put arbitrary code into the sort sub. this is
useful when you do work in one key extraction and generate multiple
keys. you can declare a temp var in init_code and assign it in one
extraction for use by later keys. in this case it can be used to set the
sort order string. this is a tested example. it prints out the sorter so
you see how the init_code is used.

#!/usr/local/bin/perl

use strict ;
use warnings ;

use Sort::Maker qw( make_sorter sorter_source ) ;

my @unsorted = (

'<table><tr><td meascode="y"></td></tr></table>',
'<table><tr><td meascode="2"></td></tr></table>',
'<table><tr><td meascode="A"></td></tr></table>',
'<table><tr><td meascode="B"></td></tr></table>',
) ;

my $sorter = make_sorter(
'GRT',
init_code => 'my $cost_order = q{A2yB};',
signed => 1,
string_data => 1,
number => q{ /code="(.)"/ && index($cost_order,$1) },
) ;

$sorter or die $@ ;

print sorter_source( $sorter ) ;

print map "$_\n", $sorter->( @unsorted ) ;


if you want the $cost_order to come from the outside, you can just use a
variable in the init_code and change it to "".

my $cost_order = 'A2yB';

my $sorter = make_sorter(
'GRT',
init_code => "my \$cost_order = '$cost_order' ;",
signed => 1,
string_data => 1,
number => q{ /code="(.)"/ && index($cost_order,$1) },
) ;

both output this:

<table><tr><td meascode="A"></td></tr></table>
<table><tr><td meascode="2"></td></tr></table>
<table><tr><td meascode="y"></td></tr></table>
<table><tr><td meascode="B"></td></tr></table>

and that appears to be the desired order.

note that i do a numeric compare on the index() value as a string compare
would fail when you get to more than 1 digit.

also see how i simplified getting the index value in the key extraction.

expressiveness like this is one of the major wins of Sort::Maker over
rolling your own sorts besides the speedup if you use the GRT.

uri

--
Uri Guttman ------ uri@xxxxxxxxxxxxxxx -------- http://www.stemsystems.com
--Perl Consulting, Stem Development, Systems Architecture, Design and Coding-
Search or Offer Perl Jobs ---------------------------- http://jobs.perl.org
.