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

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; }; }
use Test::More; sub array_it { my ($i,@arr) = (0,@_); return sub { if ( $i>$#arr ) { $i=0; return } $arr[$i++] } } { my $odo = odometer( array_it('foo','bar'), array_it('-'), array_it(3..5) ); my @o; while (defined( my $x = $odo->() )) { push @o, $x } while (my @x = $odo->()) { push @o, \@x } is_deeply \@o, ["foo-3", "foo-4", "foo-5", "bar-3", "bar-4", "bar-5", ["foo","-","3"], ["foo","-","4"], ["foo","-","5"], ["bar","-","3"], ["bar","-","4"], ["bar","-","5"] ]; } { my $odo = odometer( array_it(qw/ a b c /), array_it(qw/ 1 2 /,undef,qw/ 3 4 /), ); my @o; while (defined( my $x = $odo->() )) { push @o, $x } is_deeply \@o, [qw/ a1 a2 b3 b4 c1 c2 /]; @o = (); while (defined( my $x = $odo->() )) { push @o, $x } is_deeply \@o, [qw/ a3 a4 b1 b2 c3 c4 /]; } done_testing;

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

        > If you're working on iterator stuff, the entire chapter on that might very well be worth a read.

        Thanks, I definitely will (and had)

        HOP's way to recombine iterators is from a logical perspective unrivaled.

        Unfortunately are function calls very expensive in Perl compared to pure functional languages where call optimization kicks in.

        This can make chained iterators extremely slow ...

        Apart from this - sorry to say so - do I hate using scalar iterators with undef as termination signal.

        That's a semipredicate footgun.

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