Reputation: 1169
I'm reading the book "Higher Order Perl" by Mark Jason Dominus and I'm stuck on one of the first examples. It revolves around solving the Tower of Hanoi problem recursively, which my program does fine. The problem is, the book develops the example by refactoring the code, where rather than simply printing what the solution is doing, the solution function takes another parameter. This is a function that in my case simply does the same thing.
The problem is, I have defined my auxiliary function and I've attempted to pass it by reference, and then call it from the main function, but I am getting the following error.
Can't use an undefined value as a subroutine reference at main.pl line 86.
This version does not take a function as an argument and correctly solves the problem.
#!/usr/bin/perl
use utf8;
use v5.26;
use strict;
use warnings;
sub SolveTowerOfHanoiProblem0 {
my ($n, $start, $end, $extra) = @_;
if ($n == 1) {
say "Move disk #$n from $start to $end.";
} else {
SolveTowerOfHanoiProblem0($n - 1, $start, $extra, $end);
say "Move disk #$n from $start to $end.";
SolveTowerOfHanoiProblem0($n - 1, $extra, $end, $start);
}
}
SolveTowerOfHanoiProblem0(3, 'A', 'C', 'B');
Output:
Move disk #1 from A to C.
Move disk #2 from A to B.
Move disk #1 from C to B.
Move disk #3 from A to C.
Move disk #1 from B to A.
Move disk #2 from B to C.
Move disk #1 from A to C.
I want to refactor the code to have the line that outputs what is happening in each step to be independent of the solution code, so for the time being I wrote another function that simply does the same thing, but is passed as an argument by the caller:
sub SolveTowerOfHanoiProblem {
my ($n, $start, $end, $extra, $moveDisk) = @_;
if ($n == 1) {
$moveDisk->($n, $start, $end);
} else {
SolveTowerOfHanoiProblem($n - 1, $start, $extra, $end);
$moveDisk->($n, $start, $end);
SolveTowerOfHanoiProblem($n - 1, $extra, $end, $start);
}
}
sub PrintInstruction {
my ($disk, $start, $end) = @_;
say "Move disk #$disk from $start to $end.";
}
SolveTowerOfHanoiProblem(8, 'A', 'C', 'B', \&PrintInstruction);
I am getting the following runtime error: Can't use an undefined value as a subroutine reference at main.pl line 86
. This is where I call the passed-in function for the first time.
Line 86:
if ($n == 1) {
$moveDisk->($n, $start, $end); # <---- Here, Line 86
} else {
SolveTowerOfHanoiProblem($n - 1, $start, $extra, $end);
$moveDisk->($n, $start, $end);
I don't understand why the sub reference would be undefined. I've defined the function I'm referencing, and from what I've found so far (PerlMonks - Call subroutine by reference ?) I seem to be calling the function correctly.
I've looked at this question as well, but my PrintInstruction
function isn't defined in a module, it's defined locally, so if I understand correctly I don't need the $
symbol in the reference.
What am I missing?
Upvotes: 0
Views: 310
Reputation: 386551
You are passing the code ref to the original call to SolveTowerOfHanoiProblem
, but not to the recursive calls.
You could simply pass the code ref.
SolveTowerOfHanoiProblem($n - 1, ..., $moveDisk);
Or could remove the need to pass the same value over and over again.
sub SolveTowerOfHanoiProblem {
my $moveDisk = pop;
local *helper = sub {
my ($n, $start, $end, $extra) = @_;
if ($n == 1) {
$moveDisk->($n, $start, $end);
} else {
helper($n - 1, $start, $extra, $end);
$moveDisk->($n, $start, $end);
helper($n - 1, $extra, $end, $start);
}
};
helper(@_);
}
Same, but using a feature introduced in Perl 5.16:
use feature qw( current_sub );
sub SolveTowerOfHanoiProblem {
my $moveDisk = pop;
sub {
my ($n, $start, $end, $extra) = @_;
if ($n == 1) {
$moveDisk->($n, $start, $end);
} else {
__SUB__->($n - 1, $start, $extra, $end);
$moveDisk->($n, $start, $end);
__SUB__->($n - 1, $extra, $end, $start);
}
}->(@_);
}
Upvotes: 4
Reputation: 222652
The SolveTowerOfHanoiProblem function expects a code ref as last (fifth) argument, but you are not passing it when you invoke it in your « else » block :
SolveTowerOfHanoiProblem($n - 1, $start, $extra, $end);
Should be written as :
SolveTowerOfHanoiProblem($n - 1, $start, $extra, $end, $moveDisk);
Upvotes: 3