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

I've been reading a book "Higher-Oder Perl" by Mark Jason Dominus and I came to one of his examples on recursion but I'm having problems with it and I can't seem to see what I've typed wrong.

When i try and run this code it fails to compile. it seems from the error I'm getting that I'm trying to pass a null value as my code reference. I can't seem to see why this is though. code some one please take a look and see where my mistake is. i thought I copied every thing out of the book verbatim and it looks the same. I need a fresh set of eyes.

I've checked his website for this book and he doesn't have an example of this version it's just he suggest the passing subroutine as a suggestion in the book.

#!/usr/bin/perl use strict; use warnings; my $disk = 20; my @position = ( '', ('A') x $disk ); # disk are all initially on p +eg A sub check_move { my $i; my ( $disk, $start, $end ) = @_; if ( $disk < 1 or $disk > $#position ) { die "Bad disk number $disk. should be 1..$#position.\n"; } } sub hprint { my ( $disk, $start, $end ) = @_; print "Move disk #$disk from $start to $end\n"; } sub hanoi { my ( $n, $start, $end, $extra, $move_disk ) = @_; if ( $n == 1 ) { $move_disk->( 1, $start, $end ); } else { hanoi( $n - 1, $start, $extra, $end ); $move_disk->( $n, $start, $end ); hanoi( $n - 1, $extra, $end, $start ); } } hanoi( $disk, 'A', 'B', 'C', \&check_move ); hanoi( $disk, 'A', 'B', 'C', \&hprint );
the error I'm getting is:
Use of uninitialized value in subroutine entry at hanoi.pl line 33. Can't use string ("") as a subroutine ref while "strict refs" in use a +t hanoi.pl line 33.

Replies are listed 'Best First'.
Re: passing subroutine references
by ikegami (Patriarch) on Sep 18, 2009 at 20:56 UTC
    2 of the 4 calls to hanoi only pass 4 arguments when it expects 5.

    Update: Specifically,

    hanoi( $n - 1, $start, $extra, $end ); $move_disk->( $n, $start, $end ); hanoi( $n - 1, $extra, $end, $start );
    should be
    hanoi( $n - 1, $start, $extra, $end, $move_disk ); $move_disk->( $n, $start, $end ); hanoi( $n - 1, $extra, $end, $start, $move_disk );

    Update: Better fix: (Avoids having a million needless copies of $move_disk)

    sub hanoi { my $move_disk = pop(@_); local *_hanoi = sub { my ( $n, $start, $end, $extra ) = @_; if ( $n == 1 ) { $move_disk->( 1, $start, $end ); } else { _hanoi( $n - 1, $start, $extra, $end ); $move_disk->( $n, $start, $end ); _hanoi( $n - 1, $extra, $end, $start ); } }; &_hanoi; } hanoi( $disk, 'A', 'B', 'C', \&check_move ); hanoi( $disk, 'A', 'B', 'C', \&hprint );

      thank you all for replying. I feel kind of foolish for overlooking that. I knew it had to be something obvious I was missing.

        In my experience, it is often the simple stuff that gives the most problem. The parts of the code you aren't sure about, you will examine carefully, poking and prodding, until they give up their secrets.

        The parts that you understand, on the other hand, hide their secrets in plain sight.

        G. Wade

      I would not call that a better fix. Adds unnecessary complexity and just confuses things. If you do insist that you do not want to have to pass the $move_disk around, it's cleaner to do it like this:

      { my $move_disk; sub hanoi { $move_disk = pop(@_); goto &_hanoi; # (almost) equivalent to _hanoi(@_) } sub _hanoi { my ( $n, $start, $end, $extra ) = @_; if ( $n == 1 ) { $move_disk->( 1, $start, $end ); } else { _hanoi( $n - 1, $start, $extra, $end ); $move_disk->( $n, $start, $end ); _hanoi( $n - 1, $extra, $end, $start ); } }; } hanoi( 3, 'A', 'B', 'C', sub {print "(@_)\n"} );

      Jenda
      Enoch was right!
      Enjoy the last years of Rome.

        Your solution to getting rid of complexity is to get rid of a local and add goto &_hanoi;?

        Even a beginner needs to know local, whereas they wouldn't know goto &_hanoi;, and it adds debugging complexities at next to no benefit.

        And at what cost? You've added a global var, you've made the private recursive function public, and you've nested the main function (which seems innocent, but I've found it to cause is to confuse me time and time again).

        I beg to differ. I think you've taken 4 steps back.

        but is it really that expensive to do in my original code? aren't we just passing an address to the code, not the code itself?

Re: passing subroutine references
by kennethk (Abbot) on Sep 18, 2009 at 21:00 UTC
    Your mistakes are on lines 36 and 38. There you call hanoi with only 4 arguments instead of the necessary 5, so you lose your code references. I'm willing to bet you missed the changes to the code between pages 8 and 9 since the author didn't highlight all his changes.
Re: passing subroutine references
by Steve_BZ (Chaplain) on Sep 18, 2009 at 21:27 UTC

    Great book. I bought it earlier in the year. It really takes Perl to a whole new level. You can download it from his own site http://hop.perl.plover.com/book/

    Which is what I did at first, before giving up and buying from Amazon (which is presumably the intention of posting a free download)

    Happy reading

    Regards

      I wholeheartedly agree; great book. I, too, bought from Amazon but I bought the book before I knew it was on-line.

      However, as Steve BZ notes, the on-line version is not a good substitute for the hardcopy when you're just reading it. I was glad I had the hardcopy.

      But the on-line version is a *great* reference in my opinion. I use it a lot as I go back and rework and rework and review and try to digest all of the nuances in that book.

      I have been periodically going back to that book every several months to review and better understand different parts of it. I find the on-line version very useful and accesible in those short but deep dives into a piece of the work.

      Of course, YMMV.

      ack Albuquerque, NM