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

I've been working through examples in MJD's Higher Order Perl and hit a snag. This code from Chapter 4 works fine:
# This software is Copyright 2005 by Elsevier Inc. You may use it # under the terms of the license at http://perl.plover.com/hop/LICENSE +.txt . ### ### upto ### ## Chapter 4 section 2.1 sub upto { my ($m, $n) = @_; return sub { return $m <= $n ? $m++ : undef; }; } my $it = upto(3,5); my $value->it(); print "$value\n";
However, when I sweeten the function as suggested in the text:
sub upto { my ($m, $n) = @_; return Iterator { return $m <= $n ? $m++ : undef; }; } sub Iterator (&) { return $_[0]; }
I get a failure: Undefined subroutine &main::3 called
They laughed at Joan of Arc, but she went right ahead and built it. --Gracie Allen

Replies are listed 'Best First'.
Re: Basic question about Iterator code
by choroba (Cardinal) on May 12, 2025 at 14:18 UTC
    You probably meant
    my $value = $it->();

    instead of

    my $value->it();
    in the first sample.

    As for the error: in order to use the block syntax with a (&)-prototyped subroutine, you need to declare the subroutine before you use it. Move the last line to the top of the second sample and everything should work.

    #!/usr/bin/perl use warnings; use strict; use feature qw{ say }; sub Iterator(&) { $_[0] } sub upto { my ($m, $n) = @_; return Iterator { $m <= $n ? $m++ : undef } } my $it = upto(3,5); while (my $value = $it->()) { say $value; }

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: Basic question about Iterator code
by ikegami (Patriarch) on May 12, 2025 at 23:15 UTC

    As a side note, if you want to support returning false values (such as zero, but particularly undef) easily, return an empty list when the iterator is exhausted.

    sub upto { my ( $m, $n ) = @_; return sub { return if $m > $n; return $m++; }; }

    or

    sub upto { my ( $m, $n ) = @_; return sub { $m <= $n ? $m++ : () }; }

    Then, use a list assignment in scalar/boolean context.

    my $iter = upto( -2, 2 ); while ( my ( $ele ) = $iter->() ) { say $ele; }
    -2 -1 0 1 2

    You could have used while ( defined( my $i = $iter->() ). But you won't be able to use that for every iterators, such as one that returns the elements of an array:

    sub list { my @a = @_; return sub { @a ? shift( @a ) : () }; } my $iter = list( 1, 0, undef, "a" ); # This doesn't work: # while ( defined( my $ele = $iter->() ) ) { # say $ele // "[undef]"; # } while ( my ( $ele ) = $iter->() ) ) { say $ele // "[undef]"; }
    1 0 [undef] a

    Since you sometimes need to use the empty-list "trick", you might as well always use it for consistency. Also, it's shorter than using defined :)