Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

recursive anonymous subroutines

by Daryn (Sexton)
on Apr 06, 2006 at 19:56 UTC ( #541711=perlquestion: print w/ replies, xml ) Need Help??
Daryn has asked for the wisdom of the Perl Monks concerning the following question:

Greetings gentle monks,

is there a magic way to call an anonymous subroutine within its own body without binding it to a coderef ?

I'd like to do something like that:
(sub {... magic(...) ... )->(args)
where magic would refer to the anonymous sub being defined.

The closest I came so far was
use strict; { my $coderef; ($coderef = sub { ... $coderef->(...) ...)->(args); }
which strikes me as inelegant and potentially leaky. Here is some runnable (if silly) code :
use strict; # direct call of unbound non recursive anonymous subroutine my $res = (sub{ print @_[0] + @_[1] . "\n\n";})->(100,1); # need to bind to a coderef if recursive ? { my $coderef; ($coderef = sub {return unless $_[0] > 0; print "$_[0]\n"; $codere +f->($_[0]-1)})->(4); }

In real life, I sometimes need to call a recursive function or subroutine to process a tree-like structure. It gets called only once, at a specific point, and should (IMO) not need to be graced with a name or bound to a coderef (much in the spirit of a map {...} @somearray).

Any thoughts ? TIA.

Comment on recursive anonymous subroutines
Select or Download Code
Re: recursive anonymous subroutines
by chromatic (Archbishop) on Apr 06, 2006 at 20:01 UTC

    No.

    (Now if you had asked how to get a reference to the current subroutine, even if it's anonymous and without modifying the external code, there are ways to do that.)

      How would one do that? I'm curious.
        OK, I found a way, but you won't like it! :-)
        Use $DB::sub->() but you have to run it under the perl debuger (perl -d):
        sub { print 'A'; $DB::sub->() }->();

        Ted Young

        ($$<<$$=>$$<=>$$<=$$>>$$) always returns 1. :-)

        I'd write a little very scary, very hairy XS code. I don't know which of three or four approaches would work best, but I'd start by poking around PL_curcop.

Re: recursive anonymous subroutines
by Zaxo (Archbishop) on Apr 06, 2006 at 20:05 UTC

    No.

    That's too bad, too. I've occasionally wanted something like that myself.

    What semantics should that have, do you think? A magical local variable which points to the current block? [Added]: Or extend redo to apply to code blocks?

    After Compline,
    Zaxo

      The J language has something like that, a magical operator called $: which always refers to the innermost verb it is used at.

      However, I don't like that, and don't think it would be a good idea for perl. I'd rather like something like (one form of) the let macro in scheme. The equivalent to this would be something like this in perl: the hypothetical let NAME { BODY } ARGS could be equivalent to do { my sub NAME { BODY }; NAME(ARGS); }, where you can call NAME in the BODY too. Except we'd need a different name instead of let.

      Update: redo wouldn't help. That only works if you have tail recursion. Redo doesn't return.

        the whole point is not to name the beast. I like the idea of a $thingy :
        (sub {... $thingy->(...) ... )->(args);
        would suit me just fine. Oh well. I can live without it.

        Thanks again.
Re: recursive anonymous subroutines
by ambrus (Abbot) on Apr 06, 2006 at 20:11 UTC

    No. A code reference is the only way to use an anonymous subroutine.

    Here's an example of a recursive anonymous sub from a code I wrote earlier:

    # WRONG ... my $traverse; $traverse = sub { my(@c, $c, @m); @c = $_[0]; while (@c) { $c = pop @c; for (@{$member{$c}}) { push @m, $_; } for (@{$child{$c}}) { push @c, $_; } } @m; }; for (keys(%cut)) { my @m = &$traverse($_); my @m0 = grep { !$poison{$_} } @m; my @m1 = grep { $poison{$_} } @m; print "( ", jointab(@m0), "@ ", jointab(@m1), ")\n"; } ...

    Update: as jdporter has noticed, this example isn't recursive. It uses a stack. I was mislead by the name "traverse". Sorry.

      well the answers came even before I was able to update the original very incomplete post (first posting syndrome I guess). Many thanks to all.
Re: recursive anonymous subroutines
by stvn (Monsignor) on Apr 06, 2006 at 21:04 UTC

    In Perl 6 you will be able to do this:

    sub ($x) { return unless $x > 0; $x.say; &SUB.($x - 1); }

    -stvn
Re: recursive anonymous subroutines
by Anonymous Monk on Apr 06, 2006 at 21:18 UTC
    Maybe you'd be interested in the Y combinator.
    #!/usr/bin/perl -w use strict; print "5! = ", Y(sub{ my ($proc, $n) = @_; ($n < 2) ? 1 : $n * $proc->($proc,$n-1) }, 5), "\n"; sub Y { my ($p, $x) = @_; $p->($p,$x); }
Re: recursive anonymous subroutines
by runrig (Abbot) on Apr 06, 2006 at 21:19 UTC
    Unless it's been fixed recently, declaring anonymous recursive subs (without using Devel::Caller or the like) is leaky, so if you're declaring a lot of them (and hoping they get destroyed when they go out of scope) you can fix that with Scalar::Util::weaken:
    use Scalar::Util qw(weaken); { my ($sub, $sub1); $sub1 = $sub = sub { my $num = shift; return $num + $sub->($num-1) if $num >0; return 0; } weaken($sub); my $num = $sub->(5); print "$num\n"; }
Re: recursive anonymous subroutines
by TedPride (Priest) on Apr 06, 2006 at 22:29 UTC
    Why make life more difficult for yourself when you don't have to? You could theoretically do this without using a sub at all - any recursive function can be rewritten linearly using a stack - but I don't see you looking into that option, probably because it would be add complexity. You should follow the same reasoning when choosing whether or not to implement anonymous subs.
Re: recursive anonymous subroutines
by snoopy (Deacon) on Apr 07, 2006 at 01:30 UTC
    What's wrong storing a reference to an anonymous inner sub in an anonymous outer closure?

    Eg to create an recursive factorial subroutine:

    #!/usr/bin/perl use strict; my $sub = do { my $this_sub; $this_sub = sub { my $factor = int(shift); return $factor > 1 ? &$this_sub($factor-1) * $factor : 1; }; }; print "factorial of 7 is ".&$sub(7)."\n";
    This is localised and lends itself to functional usage, eg:
    my @factorials = map {&{ my $this_sub; $this_sub = sub { my $factor = int(shift); return $factor > 1 ? &$this_sub($factor-1) * $factor : 1; }}($_)} (3,5,7,9); print "factorials: @factorials\n";
    Update: see below for revised solution.
      "What's wrong storing a reference to an anonymous inner sub in an anonymous outer closure?"
      It's a recursive, i.e. leaking, reference.
        It's a recursive, i.e. leaking, reference.

        Hmm... probably safer to keep the reference on the call stack, thus releasing it on exit.

        For example, adopting the convention that the reference is always passed as the first parameter:

        my @factorials = map { $_[0] ||= sub { my $factor = pop; return $factor > 1 ? &{$_[0]}($_[0],$factor-1) * $factor : 1; }; &{$_[0]}($_[0],$_)} (3,5,7,9);
        ..or..
        sub make_call { goto $_[0]; } my @factorials = map { make_call (sub { my $factor = $_[1]; return $factor > 1 ? make_call($_[0], $factor-1) * $factor : 1; }, $_); } (3,5,7,9);

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://541711]
Approved by ww
Front-paged by thewalledcity
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others about the Monastery: (14)
As of 2014-08-21 20:45 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The best computer themed movie is:











    Results (143 votes), past polls