in reply to Re: Efficient but elegant Cross-Product iterator ?
in thread Efficient but elegant Cross-Product iterator ?

Thanks! :)

The code in Algorithm::Odometer::Tiny is indeed tiny, but loops over lists/arrays not subs. (like the other listed solutions)

(my title Cross Product might be misleading though ... sorry)

EDIT: I suppose though it is possible to re-init the arrays once one loop level is entered.

I wasn't able to locate the code for "forsetproduct from Math::Prime::Util" but the docs say

Our "forsetproduct" matches Set::Product in both high performance and functionality (that module's single function "product" in Set::Product is essentially identical to ours).

But that's not a lazy iterator but returning a greedy list.

Regarding Set::Product::XS , I'm more interested in PP solutions.

Cheers Rolf
(addicted to the Perl Programming Language :)
Wikisyntax for the Monastery

update

On a side note, if you want to create a string from the product, consider using the glob-iterator

DB<65> p "$_\n" while <"{1,2,3}:{a,b,c}"> 1:a 1:b 1:c 2:a 2:b 2:c 3:a 3:b 3:c

Replies are listed 'Best First'.
Re^3: Efficient but elegant Cross-Product iterator ? (updated)
by haukex (Archbishop) on Jun 21, 2020 at 09:56 UTC
    The code in Algorithm::Odometer::Tiny is indeed tiny, but loops over lists/arrays not subs. (like the other listed solutions)

    Well, part of the idea of the module was to have the code small and simple enough so that it can be copy & pasted into other code. This simple patch allows you to produce your expected output from your shown input:

    croak "no wheels specified" unless @_; - my @w = map { [ 1, ref eq 'ARRAY' ? @$_ : $_ ] } @_; + my @w = map { [ 1, ref eq 'CODE' ? $_->() : $_ ] } @_; my $done;

    You can of course do the same kind of mapping simply before calling the sub, as in odometer( map { ref eq 'CODE' ? [$_->()] : $_ } ... ), that'd allow you to use the other modules as well.

    Of course, it's a different story if the subs passed to the function are to be called as iterators themselves? Update: That'd look something like this:

    sub odometer { die "no wheels specified" unless @_; my @w = map { [ $_, $_->() ] } @_; my $done; return sub { if ($done) { $done=0; return } my @cur = map { $_->[1] } @w; for ( my $i=$#w; $i>=0; $i-- ) { defined( $w[$i][1] = $w[$i][0]->() ) and last; $w[$i][1] = $w[$i][0]->(); $done=1 unless $i; } return wantarray ? @cur : join '', map {defined()?$_:''} @cur; }; }
      Thanks, your code is indeed terser but I wouldn't really call it more elegant ;)

      I'll need to benchmark it to see if my (elegant?) referencing and shifting of the range result in a penalty. Readability (in the eye of the beholder) is my second goal.

      I don't want to loop over iterators (yet), that's again another (third) semantic.

      I need only nested loops, to make the interface intuitive for the users

      for my $x ( $sub_x->() ) { for my $y ( $sub_y->() ) { return [ $x,$y ]; } }

      From what I understand are you caching the values for the next iteration, this brakes my semantics and might cause side effects.

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery

        Thanks, your code is indeed terser but I wouldn't really call it more elegant ;)

        That's up to you ;-P

        From what I understand are you caching the values for the next iteration, this brakes my semantics and might cause side effects.

        Yes, but that can of course be rewritten. Though I don't understand how it could cause side effects if your "wheels" are not iterators themselves (unless of course you've got tied values or something like that)? My code is based on Higher-Order Perl, the algorithm is on page 131. If you're working on iterator stuff, the entire chapter on that might very well be worth a read.