Функция прохождения Perl в качестве аргумента - неопределенная ссылка - PullRequest
0 голосов
/ 22 ноября 2018

Я читаю книгу Марка Джейсона Домина "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 - Вызов подпрограммы по ссылке? ) Я, кажется, правильно вызываю функцию.

Я также рассмотрел этот вопрос , но моя функция PrintInstruction не определена в модуле, она определена локально, поэтому, если я правильно понимаю, мне не нужен символ $ вссылка.

Что мне не хватает?

Ответы [ 2 ]

0 голосов
/ 23 ноября 2018

Вы передаете код 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);
        }
    }->(@_);
}
0 голосов
/ 22 ноября 2018

Функция SolveTowerOfHanoiProblem ожидает код ссылки в качестве последнего (пятого) аргумента, но вы не передаете его, когда вызываете его в своем блоке «else»:

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

Должен быть записан как:

SolveTowerOfHanoiProblem($n - 1, $start, $extra, $end, $moveDisk);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...