Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

recursive anonymous subroutines

by Daryn (Sexton)
on Apr 06, 2006 at 19:56 UTC ( [id://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.

Replies are listed 'Best First'.
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 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 snoopy (Curate) 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);
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 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 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.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
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?Last hourOther CB clients
Other Users?
Others chilling in the Monastery: (5)
As of 2024-04-23 19:42 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found