Perl将函数作为参数传递-未定义的子引用

时间:2018-11-22 20:00:58

标签: perl

我正在阅读Mark Jason Dominus的书“ Higher Order Perl”,而我被困在第一个例子中。它围绕递归解决河内塔问题而进行,这在我的程序中还算不错。问题是,这本书通过重构代码来开发示例,其中,解决方案功能不仅仅是简单地打印解决方案在做什么,而是使用另一个参数。就我而言,这是一个功能,完全可以完成。

问题是,我已经定义了辅助函数,并试图通过引用传递它,然后从主函数调用它,但是出现以下错误。

  

不能在main.pl第86行中使用未定义的值作为子例程引用。

此版本不以函数为参数,可以正确解决问题。

#!/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');

输出:

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.

我想重构代码以使输出每一步中发生的事情的行与解决方案代码无关,所以暂时我编写了另一个功能相同的功能,但作为呼叫者的论点:

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);

我遇到以下运行时错误:Can't use an undefined value as a subroutine reference at main.pl line 86。这是我第一次调用传递函数的地方。

第86行:

if ($n == 1) {
        $moveDisk->($n, $start, $end); # <---- Here, Line 86
    } else {
        SolveTowerOfHanoiProblem($n - 1, $start, $extra, $end);

        $moveDisk->($n, $start, $end);

我不明白为什么子引用未定义。我已经定义了我要引用的函数,根据到目前为止的发现(PerlMonks - Call subroutine by reference ?),我似乎正确地调用了该函数。

我也看过this question,但是我的PrintInstruction函数未在模块中定义,而是在本地定义的,因此,如果我理解正确,我就不需要{{1 }}中的符号。

我想念什么?

2 个答案:

答案 0 :(得分:4)

您正在将代码ref传递给SolveTowerOfHanoiProblem的原始调用,而不传递给递归调用。

您只需传递代码ref。

SolveTowerOfHanoiProblem($n - 1, ..., $moveDisk);

或者可以消除一次又一次传递相同值的需要。

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(@_);
}

相同,但是使用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);
        }
    }->(@_);
}

答案 1 :(得分:3)

SolveTowerOfHanoiProblem函数希望将代码ref作为最后一个(第五个)参数,但是在«else»块中调用它时,您不会传递它:

SolveTowerOfHanoiProblem($n - 1, $start, $extra, $end);

应写为:

SolveTowerOfHanoiProblem($n - 1, $start, $extra, $end, $moveDisk);