sequel has asked for the wisdom of the Perl Monks concerning the following question:
|
---|
Replies are listed 'Best First'. | |
---|---|
Re: Ways to implement a closure
by tmoertel (Chaplain) on Oct 15, 2004 at 21:07 UTC | |
While closures are almost always created via anonymous subroutines (a la sub { ... }) at the lowest level, the real fun only comes about when you delegate this chore to some kind helper function that builds anonymous subroutines for you. Thus, in heavily functional code, you usually don't use sub { } yourself but instead rely upon a library of helpers to do the grunt work for you. For example, here's a function that builds functions to append a given suffix to strings: It takes a given suffix, remembers it, and returns a new function that will apply the remembered suffix to any string we give it: We can have even more fun if we create higher-order functions that remember other functions in their closures. Here's a pair of functions that can be used to apply other functions to a list of arguments as a whole or to each argument, one at a time: We might use them like so: We can also capture regex values in closures to good effect: Now we can use the helper function to build "regex matchers": Why would we want to make all these little functions? Because we can glue them together to make complicated things happen. One of the most fundamental kinds of glue is composition, which turns individual functions into function pipelines: Now let's glue some of our earlier functions together to make some simple pipelines: Also, note that we don't need to store our pipelines in a variable before we use them. We can call them on the fly: Note that when building pipelines using compose, the input data flows through the functions right-to-left (following mathematical convention). Watch what happens if we parenify first and then match for words: As a final example, let's build 27 different pipeline combinations from our earlier collection of functions and see what each pipeline does when applied to a reference string. To help us out, let's first build id – the identity function – which simply returns its input as its output. We'll use it to pass data through stages of our pipeline unchanged. And now, our pipeline playground: Here's the output: I hope this gives you some idea of how you can create and benefit from closures without directly having to create anonymous subroutines. Just create helper functions to do the work for you. Cheers, P.S. For a more practical fun-with-closures example, see Re: Redirecting STDOUT from internal function with 5.6.1 restrictions, which shows how to capture output from a filehandle (such as STDOUT or STDERR) temporarily. Tom Moertel : Blog / Talks / CPAN / LectroTest / PXSL / Coffee / Movie Rating Decoder | [reply] [d/l] [select] |
by pernod (Chaplain) on Oct 16, 2004 at 18:09 UTC | |
tmoertel, your functional programming tutorials in Perl continuosly boggles my mind. If you keep up like this I'll probably end up learning all I need on functional programming before I ever get around to learning Haskell. Thank you very much! fanboy pernod | [reply] |
by tos (Deacon) on Oct 18, 2004 at 08:51 UTC | |
Thanks, tos
Is simplicity best or simply the easiest Martin L. Gore | [reply] |
Re: Ways to implement a closure
by dragonchild (Archbishop) on Oct 15, 2004 at 18:42 UTC | |
get_next_counter() is "closed over" $counter. So, in this instance, get_next_counter() is a closure that is also a named subroutine. Does that help? Being right, does not endow the right to be rude; politeness costs nothing. | [reply] [d/l] |
Re: Ways to implement a closure
by ikegami (Patriarch) on Oct 15, 2004 at 19:24 UTC | |
A closure is a type of subroutine, so if you rule out anonymous subroutines, that only leaves named subroutines. dragonchild already posted an example of that. However, two things come to mind which are similiar to closures: tied variables and objects. Both can be used to execute code on hidden or encapsulated data. Refer to perlobj and perltie. | [reply] |
by tilly (Archbishop) on Oct 15, 2004 at 19:39 UTC | |
because $demonstration_data knows to stick around even though the function ended. An example of a language where the data would not be expected to stick around is C - if you don't watch out the demonstration data would be stuck on the stack, and a few function calls later would not be there to be found. | [reply] [d/l] |
by ikegami (Patriarch) on Oct 15, 2004 at 19:48 UTC | |
I see. Well there you go, yet another way! Nit: that code works in C (given some imagined Hash class), and the data does stick around:
I think you meant this:
Which wouldn't work in C:
| [reply] [d/l] [select] |
by SpanishInquisition (Pilgrim) on Oct 18, 2004 at 19:50 UTC | |
by ikegami (Patriarch) on Oct 18, 2004 at 20:05 UTC | |
by ihb (Deacon) on Oct 16, 2004 at 13:42 UTC | |
I disagree with it being a closure, even using the broader definition of a closure. If you mean that a closure (in Perl) is anything that increases reference count anywhere, then I understand you, but must say that that's the broadest definition of a closure I've ever heard. With that definition though, this below would be equally much a closure: If you mean that it's a closure because you use a lexical variable in there, I disagree that it's a closure. Adding that it must close over/bind a lexical environment (which is what some people think and I think you are referring to), the returned hash reference is not a closure because it doesn't bind anything in its lexical surrounding. $demonstration_data knows to stick around even though the function ended In fact, it doesn't. The value it references knows to stay around. The instance of $demonstration_data doesn't stay around itself. Changing $demonstration_data after the latter hash reference is created does not change anything in it. This code below illustrates that. The hash value holds a RV that references the same PVHV as $demonstration_data references. $demonstration_data and demonstration()->{demonstration_data} are different RVs, but reference the same value. One can argue that the reference operator \ can act as a closure. Looking at lexical variables, it binds the current instance of the lexical variable. The similarity between \ and an anonymous closure and the contrast to other scalar types is demonstrated below. Your hash above is no more closure than the array reference here. Neither the hash or the array bind the variables used in it - they store copies of the values the variables hold. In your case the value is a reference but that is no different from any other non-reference value except that it increases a ref count somewhere when copied, but that has nothing to do with closing over any environment. One may argue that the array reference itself is a closure and so may be, if you look at [ LIST ] as syntactic sugar for do { my @foo = LIST; \@foo }. That does not make any data structure keeping a reference to the anonymous array a closure though. It's just a data structure that holds a closure. ihb Read argumentation in its context! | [reply] [d/l] [select] |