baxy77bax has asked for the wisdom of the Perl Monks concerning the following question:

help needed,

well here is the problem, i want to make the below code work. the idea is that i want to pass some argument into sub which then does what it does and passes the result in the array that is every time the foreach func starts canceled to no arguments. i could do it without strict pragma but the strict pragma has to stay. example:

use strict; %hash -> here i have some hash my @array = qw(...); my $a = 1; foreach my $r (@array){ walk($r); @big;<----------------------------------- foreach my $b (@big){ | $a++; | print " I am $b that loves $a\n"; | } | } | | | sub walk { | | my $i = shift; | push @big, $i;------------------------------- for (@{$hash{$i}}){ walk($_) }
this example doesn't work because of @big (it should be: my @big). i tried to make it our @big but the logic is not on my side.

thnx

Replies are listed 'Best First'.
Re: subroutine recursion question
by Corion (Patriarch) on Jun 20, 2008 at 08:21 UTC

    I don't really understand what you're doing respectively what your logic is concerning @big. My usual approach to having a recursive subroutine return a list is to either pass around a reference to the result set or to collect the results of the branches:

    # Make walk() return a list and collect the results: sub walk { my ($i) = @_; my @local_results = $i; for (@{$hash{$i}}) { push @local_results, walk($_); }; return @local_results; }; ... foreach my $r (@array) { my @big = walk($r); ... };

    The alternative is easier if your tree walking somehow has to check for duplicate elements, so it's more an optimization:

    # Make walk() augment the result list: sub walk { my ($i,$results) = @_; # If we're called with just one argument, we need to return a fres +h list $results ||= []; push @$results, $i; for (@{$hash{$i}}) { walk($_,$results); # here, we don't care about the results of +walk() }; return @$results; }; ... foreach my $r (@array) { my @big = walk($r); ... };
Re: subroutine recursion question
by ikegami (Patriarch) on Jun 20, 2008 at 08:24 UTC

    Clear it before calling walk.

    foreach my $r (@array){ my @big; walk($r); ... } sub walk { my $i = shift; push @big, $i; for (@{$hash{$i}}){ walk($_) } }

    However, it's bad practice for a sub to modify some private variable somewhere. Fix:

    foreach my $r (@array){ my @big; walk(\@big, $r); ... } sub walk { my $big = shift; my $i = shift; push @$big, $i; for (@{$hash{$i}}){ walk($big, $_) } }
    or
    foreach my $r (@array){ my @big = walk($r); ... } sub walk { my $i = shift; my @big = $i; for (@{$hash{$i}}){ push @big, walk($_); } return @big; }
Re: subroutine recursion question
by pc88mxer (Vicar) on Jun 20, 2008 at 08:23 UTC
    How about just clearing @big before you make the top the top-level call to walk(). Is that what you want to do?
    our @big; # "my" will also work here for my $r (@array) { @big = (); walk($r); # @big lists every node visited by walk($r) ...do something with @big... } sub walk { my $i = shift; push(@big, $i); for (...) { walk($_) } }
      our??? Without changing anything else, it would work with my.
        Yeah, you're right about that. I have to remind myself that my variables can be shared between subroutines. For some reason, when I encounter that situation I think "global" variable which translates into our.
Re: subroutine recursion question
by moritz (Cardinal) on Jun 20, 2008 at 09:00 UTC
    I know this is of little practical help to you, but I want to point out a really elegant way of how to do this in Perl 6:
    my @big = gather { walk() } for @big -> $b { ... } sub walk { # do stuff here; take $result; walk(); # recurse into self }

    The take is dynamically scoped, so you can call it in a different sub, in a recursive sub etc. Additionally it has the benefit of returning a lazy list, which means that the computation is only carried out if the data is really needed.

    I imperfectly back-ported that behaviour to perl 5 with the module Perl6::GatherTake.