Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

RE: RE (tilly) 1: Fly Subroutines on the Fly

by BlaisePascal (Monk)
on Sep 19, 2000 at 06:30 UTC ( [id://33050]=note: print w/replies, xml ) Need Help??


in reply to RE (tilly) 1: Fly Subroutines on the Fly
in thread Fly Subroutines on the Fly

"I am only an egg"

What do you mean by "Where closures rock is as a way to provide optional hooks when you compile a function without paying any run-time price if the hook was not included".

Could you provide an example of what you mean?

And why is eval being used here at all? Wouldn't the following be simpler, faster, and easier to maintain?

sub make_divide # "Half" is a lousy name for a curried divide { my $n = shift; if (Power_Of_Two($n)) { my $log = Log2($n); return sub { return $_[0] >> $log; }; } else { return sub { return int( $_[0] / $n ); }; } }
That's what I'd call a closure (well, the subroutines returned anyway, make_divide itself isn't one, but it's return values are).

So, as I so often ask, what am I missing?

Replies are listed 'Best First'.
RE: RE: RE (tilly) 1: Fly Subroutines on the Fly
by chromatic (Archbishop) on Sep 19, 2000 at 07:09 UTC
    I could imagine a database access object that was actually a blessed coderef to a SQL query.

    You might write the method generally enough that it could return the first appropriate match for the query, or all matches.

    With a closure (which will be blessed), you could bind the query to operate on a single table (or a specific query string or whatever)... and you could choose to return a single value from the query or a listref or a hashref.

    That's a contrived example, yes, but I could imagine using it somewhere.

RE (tilly) 3: Fly Subroutines on the Fly
by tilly (Archbishop) on Sep 19, 2000 at 15:23 UTC
    You are right that the example given is not one where a closure would be appropriate, which is why I pointed out the overhead of entering a function.

    I don't have the energy to write a real example to show you what I mean, but I can point at a case which would have been helped by it. Download Pod::Parser and in it look for the implementation of parse_paragraph. There are a lot of optional hooks provided, and the way it was done is to pass all of the data through a series of functions then keep on checking flags. But most of the time those functions (like preprocess_line and preprocess_paragraph) don't do anything.

    I once looked at it closely, and couldn't tie out the current API. But if you had an API where you created all of the hooks you wanted then asked it to build a parser, it could then create a function, and then once (and once only) decide to only include lines with optional hooks if you were using it. The result would do the same task several times faster since optional hooks would not be present in the final output. (Without having looked I would suspect that Parse::RecDescent does this.)

    As for why the eval, to do what I describe you would want to build up the string, as you walk through checking once for the hook then including or not including bits of what could be part of your sub, then call eval to have it return the final subroutine for you.

      You are right that the example given is not one where a closure would be appropriate, which is why I pointed out the overhead of entering a function.

      I think we may have different definitions of "closures". I think of a closure as a subroutine (anonymous or named) that captures the lexical scope of a variable, like the two anonymous divide routines in my code. The original code by Adam and your fix of it don't capture lexical scope; the values of $n are interpolated before eval ever sees it.

      You seem to be talking about run-time compilation of new subroutines. While that can be a powerful technique, it is different than closures (to me, at least).

        Exactly. The example I gave is not a closure, no one suggested it was. In fact, I specifically said I was trying this instead of a closure.

        Ok, maybe I wasn't That specific. What I said was, "I'm not sure how to do this using a closure (short of a big ugly if-then-else structure) so I tried to create subroutines..." which I read, perhaps mistakenly, "I didn't like the use of a closure so I tried something else".     I apologize if this confused anyone.

        You seem to be talking about run-time compilation of new subroutines. While that can be a powerful technique, it is different than closures (to me, at least).
        Yes... casually, people say "closures" when they mean "anonymous subroutines". But properly speaking, a subroutine (anonymous or not) is not a closure unless it also captures lexical state that can go out of scope. See Closures (was Re: for loops) for an example of detection.

        -- Randal L. Schwartz, Perl hacker

RE: (3) (tilly) 1: Fly Subroutines on the Fly
by Adam (Vicar) on Sep 19, 2000 at 19:37 UTC
    That's only easier to maintain if your subroutines are as simple as just doing division. But mine arn't, they are actually about a dozen or so lines of code, and only have a few points of optimization. It would be silly to have an IFTE structure returning one of 6 or 7 different possabilities... I would have to apply a simple change to every possability rather then one string. That becomes a maintanance nightmare. Hence the question in the first place... what is the best way to assemble an anonymous subroutine on the fly. (I am also curious how CGI.pm does it, since those subroutines are actually named!)

    Tilly gave an answer as to why my eval was failing, but he then went on to confuse me with his disapproval of my method followed by a description of what I should do (which was exactly what I thought I WAS doing). I am somewhat confused by that, and perhaps someone can enlighten me.

      What I didn't think was worthwhile is worrying about which low-level operation you can get away with which times. Instead analyze your code for steps that may or may not ever be needed, and only include the ones that you are using this time.

      This results in a structure with a lot of single level if statements, which avoids the explosion of possible combinations. Flexibility without undue code complication and without paying run-time performance except for the steps you need.

      As for what CGI does, the trick is that if you have a function in a package named AUTOLOAD, then it will be called on any unknown functions or method calls. $AUTOLOAD is the name of the unknown method. So what happens is that he creates a hash of possible methods he may or may not ever need, and an AUTOLOAD function. Upon your first trying to call a function or method he creates it on the fly and runs it. Upon your second try the method is already there, and is called directly. If you need all of the methods this is slower than just compiling directly. If you only need a few then this is faster.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others chilling in the Monastery: (4)
As of 2024-03-28 23:18 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found