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

I was using a code from "Perl Hacks", hack-27, but it doesn't works as
expected, and I don't know why.

Any one can explain what it should yield ?
Or am I missing something?

#!/usr/local/bin/perl use strict; use warnings; use Want 'howmany'; sub multi_iterator { my ($iterator)=@_; return sub{ my $context=wantarray(); return unless defined $context; return $iterator->() unless $context; return map { $iterator->() } 1 .. howmany(); }; } sub counter { my ($from, $to, $step)= @_; $step ||= 1; return sub { return if $from > $to; my $value=$from; $value +=$step; return $value; }; } #main my $counter=counter(1, 10, 3); my $iterator= multi_iterator($counter); my ($un, $deux)=$iterator->(); my $trois=$iterator->(); #should print 1, 4, 7 print "Hack-27 ", $un, ",", $deux, ",", $trois, "\n";
-- cmic. retired sysadmin and Perl user..

Replies are listed 'Best First'.
Re: iterator w/ wantarray()
by haukex (Archbishop) on Apr 25, 2020 at 19:17 UTC

    Put print statements into your subs (Basic debugging checklist), and you'll see that $value will never change, since all that that sub is returning is $from+$step every time it's called. You have to move the my $value=$from; outside of that sub so that it will close over it (closures).

    By the way, Want seems to mess with the internals, and it has several open bugs in regards to segfaults. The typical approach to iterators is to have them return undef at the end of the sequence, and you can also overload the <> operator, as I did in Algorithm::Odometer::Tiny (note that overloading <> in list context only works on Perl 5.18 and up). By the way, the book Higher-Order Perl is an excellent read in regards to iterators and all the things you can do with them.

    use warnings; use strict; use Want 'howmany'; sub multi_iterator { my ($iterator) = @_; return sub { my $context = wantarray(); print "multi_iterator called, <$context>\n"; return unless defined $context; return $iterator->() unless $context; return map { $iterator->() } 1 .. howmany(); }; } sub counter { my ($from, $to, $step) = @_; $step ||= 1; my $value = $from; return sub { print STDERR "counter called, $from / $to / $step / $value\n"; return if $value > $to; my $v = $value; $value += $step; return $v; }; } my $counter = counter(1, 10, 3); my $iterator = multi_iterator($counter); my ($un, $deux) = $iterator->(); my $trois = $iterator->(); print "Hack-27 ", $un, ",", $deux, ",", $trois, "\n"; __END__ multi_iterator called, <1> counter called, 1 / 10 / 3 / 1 counter called, 1 / 10 / 3 / 4 multi_iterator called, <> counter called, 1 / 10 / 3 / 7 Hack-27 1,4,7

      Problem solved

      I realized that it is a closure!

      You are right. The sub counter is modified like this and it works OK.
      Thank you for your help.

      # # My fixed counter() # sub counter { my ($from, $to, $step)= @_; $step ||= 1; return sub { return if $from > $to; my $value=$from; $from +=$step; return $value; }; }
      -- cmic. Life helps. Perl Too.

        This would be a more normal way of doing it. Don't change $from but close over $value.

        # # My fixed counter() # sub counter { my ($from, $to, $step) = @_; $step //= 1; my $value = $from; return sub { return if $value > $to; $value += $step; }; }
Re: iterator w/ wantarray()
by stevieb (Canon) on Apr 25, 2020 at 19:00 UTC

    It would be prudent of you to at least explain what the expected results should be, and the results you're getting.

    "It doesn't work as I expect" isn't helpful.

      What's wrong with:

      #should print 1, 4, 7

      Greetings,
      -jo

      $gryYup$d0ylprbpriprrYpkJl2xyl~rzg??P~5lp2hyl0p$

        I don't normally look for expected results in a comment at the bottom of the code. It should be expressed along with the problem statement.

        Beyond that, I don't even look at code if the OP doesn't provide a problem statement, expected, and actual results up front. Why look at code to solve a problem for someone, if they don't at least state what's expected from it clearly? I might find 30 issues to fix, but I've wasted my time if I didn't fix the one thing that was broken to them.