Search code examples
functionrecursionsubroutineperl

Reason my subroutine won't recurse


#!/usr/bin/perl
use strict;
use warnings;
use List::MoreUtils 'uniq';

my %functiontable =();
$functiontable{foo} = \&foo;

sub iterate {
  my ($function, $iterations, $argument) = @_;
  return $argument unless 0 < $iterations;
  return $argument unless $function = $functiontable{$function};
  my @functioned = $function->($argument);
  my @refunctioned = ();
  for my $i (0 .. @functioned - 1) {
    push @refunctioned, iterate ($function, ($iterations - 1), $functioned[$i]);
  }
  return uniq @refunctioned;
}

sub foo {
  my ($argument) = @_;
  my @list = ($argument, $argument.'.', $argument.',');
  return @list;
}

my @results = iterate 'foo', 2, 'the';
print "@results";

This prints the the. the,, i.e. it doesn't iterate (recurse). I would expect it to print the the. the, the.. the., the,. the,,.

(I used Smart::Comments to check whether it enters iterate a second time, and it does, but it doesn't seem to do everything in the function.)

I can't figure out why. Can someone please help me figure out why, or propose a fix?


Solution

  • The first time your subroutine iterate is called it translates the subroutine name in $function from a name to a subroutine reference

    So the first time iterate calls itself it is passing the subroutine reference, and the line

    return $argument unless $function = $functiontable{$function};
    

    will stringify the reference and attempt to find an element of the hash using a key something like CODE(0x23e0838)

    Clearly that element doesn't exist, so your unless fails and $argument is returned immediately without continuing the recursion



    Update

    I would write something like this

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    use 5.10.0;
    
    my %functions = ( foo => \&foo );
    
    sub iterate {
        my ($func, $arg, $depth) = @_;
        return $arg unless $depth;
        map {iterate($func, $_, $depth - 1); } $functions{$func}->($arg);
    }
    
    sub foo {
        my ($arg) = @_;
        map "$arg$_", '', '.', ',';
    }
    
    my @results = iterate('foo', 'the', 2);
    say "@results";
    

    output

    the the. the, the. the.. the., the, the,. the,,