in reply to Re (tilly) 3: How to pass a variable to function selected from a hash
in thread How to pass a variable to function selected from a hash

What a great example Tilly! This really got my imagination going. When I first looked at your line of code:
print join ",", map { &{$field_info{$_}{format}}($deal) } @fields;

I had no idea where to begin. I couldn't see anything wrong at first. Thankfully, you're post, and the subject at hand, gave enough information that I could infer/guess what the other data structures might look like. In order to optimize your code, I had to imagine & construct the code that wraps around it, in order to simulate it's environment.

After doing some simple code, I set to look at optimizations. Right away nothing jumped out at me. I thought about hash slices, not using map, using the arrow operator, replacing join with $, and more; some of them worked, others didn't.

Then, I remembered that we are looping over a set of deals. So I made an @deals array, and started to loop over it. Then I saw it. The map was taking the @fields, and was dereferencing the subref's each time through the @deals loop. Same thing each time through. Now, assuming that @fields is not modified in the loop, we can factor it out, and do it a single time outside of the @deals loop.

There are more optimizations I could make to my code, but I didn't want to stray too far from attempting to optimize Tilly's code, and loose the point of the excercise.

Anyhow, this is what I came up with:
#!/usr/bin/perl -w use strict; #I am sure there are more fields than this my @fields = qw(a b); #Subroutines that return a value. I imagine #you had alot more processing in here, but #this is all make-believe =) my %field_info = ( a => { format => sub {return $_[0]->{deep}{inside}{a}} }, b => { format => sub {return $_[0]->{deep}{inside}{b}} }, ); #Had to simulate a reasonable amount of data, that could #potentially cause a slowdown #Let's make @ deals my @deals; for(my $i = 0; $i < 10000; $i++) { push @deals, { deep => { inside => { a => "a[$i]", b => "b[$i]"} } } +; } #Force commas to be the field seperator inside #print(), so we can drop the join(), and pass an array #to print, rather than one long "joined" string - it should be faster local $, = ','; #Set the output record seperator, so that each call to #print tags on a "\n" after printing out the $deal local $\ = "\n"; #Dereference the subroutines, so that we aren't doing a hash traversal #each time through the @deals loop AND the map. my @dereferenced_subs = map { $_->{format} } @field_info{ @fields }; #Print out each $deal foreach my $deal (@deals) { print map { $_->($deal) } @dereferenced_subs; }

How's that, did I miss anything?

Update: I found one optimization, although it could be at the expense of readability. The part where I dereference the subs before going into a loop, I changed to a hash slice to improve the speed of the hash lookup, if there was alot of fields this could make a larger difference. Also, I added a \n after each print, so that the information could be pretty printed.

Replies are listed 'Best First'.
Re (tilly) 5: How to pass a variable to function selected from a hash
by tilly (Archbishop) on Jan 18, 2001 at 13:10 UTC
    Yup, looks like you got it all. Other than the missed join in your print. And the return on the next line. Oh, and the fields in question were actually printed on the first line, but that is neither here nor there. :-)

    Repeated nested lookups happen at runtime in Perl, and you can often get signficant speedups by factoring them out of loops. Also I have found that just creating temporary variables for them makes the mechanics of the code less of a problem, and clarifies what it is doing.

    BTW the original example actually created a set of reports, with different sets of fields. And some of the fields themselves were the result of complex calculations. (Many of which resembled each other and...but I digress.) Being able to move the logic off to elsewhere considerably simplified the overall design and made minor tweaks and changes a lot easier to implement. The basic idea was indeed a good fit, even if it was not a good way to first meet this approach. :-)

    UPDATE
    Oops, you did indeed get the join correct. And even commented it. I missed that last night, sorry.