Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number

Comment on

( #3333=superdoc: print w/replies, xml ) Need Help??
(Updated 20041017: Added final, put-it-all-togther example.)

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:

sub make_suffix_appender { my $suffix = shift; return sub { return $_[0] . $suffix; } }
It takes a given suffix, remembers it, and returns a new function that will apply the remembered suffix to any string we give it:
my $bang_appender = make_suffix_appender("!"); print $bang_appender->("Hello"); # Hello! print $bang_appender->("Dude"); # Dude!
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:
sub apply { my $fn = shift; return sub { $fn->(@_); } } sub apply_to_each { my $fn = shift; return sub { map { $fn->($_) } @_; } }
We might use them like so:
my $upcase = apply( sub { uc("@_") } ); print $upcase->("Hello"); # HELLO print $upcase->("Hello", "world"); # HELLO WORLD my $parenify_all = apply( sub { "(@_)" } ); my $parenify_each = apply_to_each( $parenify_all ); print $parenify_all ->("Hello", "world"); # (Hello world) print $parenify_each->("Hello", "world"); # (Hello)(world)
We can also capture regex values in closures to good effect:
sub make_regex_matcher { my $regex = shift; return sub { local $_ = $_[0]; /$regex/g; } }
Now we can use the helper function to build "regex matchers":
my $digit_matcher = make_regex_matcher( qr/(\d+)/ ); my @digits = $digit_matcher->( "123-blah-45-6789" ); print "@digits"; # 123 45 6789 my $word_matcher = make_regex_matcher( qr/(\w+)/ ); my @words = $word_matcher->( "123-blah-45-6789" ); print "@words"; # 123 blah 45 6789
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:

sub compose2 { my ($f, $g) = @_; return sub { $f->( $g->( @_ ) ); } }; use List::Util qw( reduce ); sub compose { no warnings qw( once ); reduce { compose2($a,$b) } @_; }
Now let's glue some of our earlier functions together to make some simple pipelines:
my $up_banger = compose($bang_appender, $upcase); print $up_banger->("Hello"); # HELLO! my $upcase_words = compose($upcase, $word_matcher); print $upcase_words->( "123-blah-45-6789" ); # 123 BLAH 45 6789 my $parenify_words = compose($parenify_each, $word_matcher); print $parenify_words->( "123-blah-45-6789" ); # (123)(blah)(45)(6789)
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:
print compose($parenify_each, $word_matcher)->("a b c"); # (a)(b)(c)
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:
print compose($word_matcher, $parenify_each)->("a b c"); # abc
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.
my $id = sub { wantarray ? @_ : $_[0] };
And now, our pipeline playground:
my $fs = 0; for my $f ($id, $parenify_all, $parenify_each) { my $fd = (qw( id- pAl pEa ))[$fs++]; my $gs = 0; for my $g ($id, $word_matcher, $digit_matcher) { my $gd = (qw( id- wrd dig ))[$gs++]; my $hs = 0; for my $h ($id, $upcase, $bang_appender) { my $hd = (qw( id- up- bng ))[$hs++]; print "$fd-$gd-$hd: ", compose($f,$g,$h)->("Eat more 3.14"); } } }
Here's the output:
id--id--id-: Eat more 3.14 id--id--up-: EAT MORE 3.14 id--id--bng: Eat more 3.14! id--wrd-id-: Eatmore314 id--wrd-up-: EATMORE314 id--wrd-bng: Eatmore314 id--dig-id-: 314 id--dig-up-: 314 id--dig-bng: 314 pAl-id--id-: (Eat more 3.14) pAl-id--up-: (EAT MORE 3.14) pAl-id--bng: (Eat more 3.14!) pAl-wrd-id-: (Eat more 3 14) pAl-wrd-up-: (EAT MORE 3 14) pAl-wrd-bng: (Eat more 3 14) pAl-dig-id-: (3 14) pAl-dig-up-: (3 14) pAl-dig-bng: (3 14) pEa-id--id-: (Eat more 3.14) pEa-id--up-: (EAT MORE 3.14) pEa-id--bng: (Eat more 3.14!) pEa-wrd-id-: (Eat)(more)(3)(14) pEa-wrd-up-: (EAT)(MORE)(3)(14) pEa-wrd-bng: (Eat)(more)(3)(14) pEa-dig-id-: (3)(14) pEa-dig-up-: (3)(14) pEa-dig-bng: (3)(14)
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.


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.

In reply to Re: Ways to implement a closure by tmoertel
in thread Ways to implement a closure by sequel

Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":

  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.
  • Log In?

    What's my password?
    Create A New User
    and all is quiet...

    How do I use this? | Other CB clients
    Other Users?
    Others rifling through the Monastery: (5)
    As of 2018-01-21 17:33 GMT
    Find Nodes?
      Voting Booth?
      How did you see in the new year?

      Results (228 votes). Check out past polls.