in reply to "a bit of sugar "(HOP)

Some examples may help...

I would write an iterator without either NEXTVAL() or Iterator() myself. Something like the following:

#!/usr/bin/perl -w use strict; use warnings; sub make_rand { my $x = 0; return sub { return $x++; # not very random }; } my $rng = make_rand(); for my $i (1..4) { my $x = $rng->(); print "$x\n"; }

sub {BLOCK} produces a reference to an anonymous subroutine and make_rand() uses this form as argument to return, returning a reference to an anonymous subroutine.

$rng->() dereferences the subroutine reference and calls the referenced subroutine.

NEXTVAL() does the same thing, but in a subroutine instead of directly. So the following also works:

#!/usr/bin/perl -w use strict; use warnings; sub NEXTVAL { $_[0]->() } sub make_rand { my $x = 0; return sub { return $x++; }; } my $rng = make_rand(); for my $i (1..4) { my $x = NEXTVAL ( $rng ); print "$x\n"; }

Iterator() deals with return sub {block}. You can define Iterator() without the prototype, in which case you would have to explicitly produce a subroutine reference as its argument. The following works:

#!/usr/bin/perl -w use strict; use warnings; sub NEXTVAL { $_[0]->() } sub Iterator { return $_[0] } sub make_rand { my $x = 0; return sub { return Iterator sub { return $x++; }; } my $rng = make_rand(); for my $i (1..4) { my $x = NEXTVAL ( $rng ); print "$x\n"; }

In this case, the reference to the anonymous subroutine is an argument to the subroutine Iterator rather than return, which only sees what Iterator returns. As Iterator returns its first argument without changing it, the reference to the anonymous subroutine is still being returned from make_rand().

Finally, with a prototype on Iterator(), you can dispense with the "sub" in its argument list and simply provide a "{BLOCK}". So the following also works.

#!/usr/bin/perl -w use strict; use warnings; sub NEXTVAL { $_[0]->() } sub Iterator (&) { return $_[0] } sub make_rand { my $x = 0; return Iterator { return $x++; }; } my $rng = make_rand(); for my $i (1..4) { my $x = NEXTVAL ( $rng ); print "$x\n"; }

They all work. The first will be slightly more efficient at run time, but usually not enough to matter. The latter may, depending on your sensibilities, be easier to understand and maintain. You can take your pick and use whichever tickles your fancy.

Replies are listed 'Best First'.
Re^2: "a bit of sugar "(HOP)
by Wiggins (Hermit) on Mar 14, 2009 at 14:43 UTC
    Thank you! +++++ Your progression of equivalent examples showed/taught me what was going on in that creation of an iterator.
    • example #1 was perfectly clear; that, I was familiar with.
    • #2 was still not a stretch.
    • #3 is where the "Ah Ha" moment came and the I realized that this was a 'labeling' technique.
    • #4 was just another layer of obsfication.

    By the time I was reading the responses, I had gotten to the imap amd igrep examples in HOP, and it all came together.

    This does make me wonder how many books one would have to read to find, and understand, all of these little special cases that exist within the language?

    Thanks again..

    It is always better to have seen your target for yourself, rather than depend upon someone else's description.

      This does make me wonder how many books one would have to read to find, and understand, all of these little special cases that exist within the language?

      For my part, I won't live long enough to learn them all. Following Perl Monks and trying to understand and answer questions has taught me more than I learned in many years of using perl.