in reply to Efficient but elegant Cross-Product iterator ?

My Algorithm::Odometer::Tiny has some code, in addition to a "See Also" section with quite a few references to other modules. If you want speed, forsetproduct from Math::Prime::Util or Set::Product::XS are probably going to be best.

Replies are listed 'Best First'.
Re^2: Efficient but elegant Cross-Product iterator ?
by LanX (Saint) on Jun 20, 2020 at 17:23 UTC
    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
      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