Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

Re: Re: How to pass a variable to function selected from a hash

by cat2014 (Monk)
on Jan 18, 2001 at 04:27 UTC ( [id://52648]=note: print w/replies, xml ) Need Help??


in reply to Re: How to pass a variable to function selected from a hash
in thread How to pass a variable to function selected from a hash

Sweet! That works- thank you!

I'd always been told that shifting off my input was a Bad Horrible Not Good thing to do, but I guess that in this case, this is better.

  • Comment on Re: Re: How to pass a variable to function selected from a hash

Replies are listed 'Best First'.
Re (tilly) 3: How to pass a variable to function selected from a hash
by tilly (Archbishop) on Jan 18, 2001 at 06:25 UTC
    Who told you that? I certainly do not agree that that is true in general.

    Using shift to handle fixed arguments is a standard idiom in Perl. It sometimes not the clearest way to express yourself, but avoiding is not always better either. Use it when it fits. Sometimes that is a matter of taste. In this case I would use it.

    For more on what bit you, see Arrays are not lists.

    For your actual problem you may be glad to find out that there is no need to actually name your handler functions. Just use sub to declare them. When combined with closures (as explained in perlref) this leads to some powerful programming techniques which are as different from standard procedural and OO programing as they are from each other. For more on that I recommend some of my posts that touch on functional techniques (a couple of which are on my home node) and visiting MJD's site and looking around.

    UPDATE
    I would just like to mention that the way to first encounter functional programming is not how I did it. I was told to find out why a several-hundred line program was slow. I spent a good chunk of a day reading something written by a tired programmer in a hurry. When I finally figuring out that a key step was the line that read:

    print join ",", map { &{$field_info{$_}{format}}($deal) } @fields;
    I was far from pleased. And even though I have since learned to like the technique and use it, I make sure that anyone who will have to read my code gets a much gentler introduction to how it works. :-)

    BTW I was successful in speeding the program up. In fact there is a significant performance mistake in the above line. Given that $deal is a data structure and we are looping over a set of deals, what improvement can you find?

      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.

        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.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://52648]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others contemplating the Monastery: (3)
As of 2024-04-26 03:45 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found