At this point, I might as well include the code...
So here it is (minus the POD) and a link to the copy on search.cpan.org.
Most of the routines include improvements over the versions available on PerlMonks. You might also want to check out the example scripts included with the module.
package Algorithm::Loops; # The command "perldoc Algorithm::Loops" will show you the # documentation for this module. You can also seach for # "=head" below to read the unformatted documentation. use strict; BEGIN { # Some still don't have warnings.pm: if( eval { require warnings } ) { warnings->import(); if( eval { require warnings::register; } ) { warnings::register->import(); } } else { # $^W= 1; } } require Exporter; use vars qw( $VERSION @EXPORT_OK ); BEGIN { $VERSION= 1.031_00; @EXPORT_OK= qw( Filter MapCar MapCarE MapCarU MapCarMin NestedLoops NextPermute NextPermuteNum ); { my @nowarn= ( *import, *isa ) } *import= \&Exporter::import; *isa= \&UNIVERSAL::isa; } sub _Type { my( $val )= @_; return ! defined($val) ? "undef" : ref($val) || $val; } sub _Croak { my $depth= 1; my $sub; do { ( $sub= (caller($depth++))[3] ) =~ s/.*:://; } while( $sub =~ /^_/ ); if( eval { require Carp; 1; } && defined &Carp::croak ) { unshift @_, "$sub: "; goto &Carp::croak; } die "$sub: ", @_, ".\n"; } sub Filter(&@) { my( $code, @vals )= @_; isa($code,"CODE") or _Croak( "No code reference given" ); # local( $_ ); # Done by the loop. for( @vals ) { $code->(); } wantarray ? @vals : join "", @vals; } sub MapCarE(&@) { my $sub= shift(@_); isa($sub,"CODE") or _Croak( "No code reference given" ); my $size= -1; for my $av ( @_ ) { isa( $av, "ARRAY" ) or _Croak( "Not an array reference (", _Type($av), ")" ); if( $size < 0 ) { $size= @$av; } elsif( $size != @$av ) { _Croak( "Arrays with different sizes", " ($size and ", 0+@$av, ")" ); } } my @ret; for( my $i= 0; $i < $size; $i++ ) { push @ret, &$sub( map { $_->[$i] } @_ ); } return wantarray ? @ret : \@ret; } sub MapCarMin(&@) { my $sub= shift(@_); isa($sub,"CODE") or _Croak( "No code reference given" ); my $min= -1; for my $av ( @_ ) { isa( $av, "ARRAY" ) or _Croak( "Not an array reference (", _Type($av), ")" ); $min= @$av if $min < 0 || @$av < $min; } my @ret; for( my $i= 0; $i < $min; $i++ ) { push @ret, &$sub( map { $_->[$i] } @_ ); } return wantarray ? @ret : \@ret; } sub MapCarU(&@) { my $sub= shift(@_); isa($sub,"CODE") or _Croak( "No code reference given" ); my $max= 0; for my $av ( @_ ) { isa( $av, "ARRAY" ) or _Croak( "Not an array reference (", _Type($av), ")" ); $max= @$av if $max < @$av; } my @ret; for( my $i= 0; $i < $max; $i++ ) { push @ret, &$sub( map { $_->[$i] } @_ ); } return wantarray ? @ret : \@ret; } sub MapCar(&@) { my $sub= shift(@_); isa($sub,"CODE") or _Croak( "No code reference given" ); my $max= 0; for my $av ( @_ ) { isa( $av, "ARRAY" ) or _Croak( "Not an array reference (", _Type($av), ")" ); $max= @$av if $max < @$av; } my @ret; for( my $i= 0; $i < $max; $i++ ) { push @ret, &$sub( map { $i < @$_ ? $_->[$i] : () } @_ ); # If we assumed Want.pm, we could consider an early return. } return wantarray ? @ret : \@ret; } sub NextPermute(\@) { my( $vals )= @_; my $last= $#{$vals}; return !1 if $last < 1; # Find last item not in reverse-sorted order: my $i= $last-1; $i-- while 0 <= $i && $vals->[$i] ge $vals->[$i+1]; # If complete reverse sort, we are done! if( -1 == $i ) { # Reset to starting/sorted order: @$vals= reverse @$vals; return !1; } # Re-sort the reversely-sorted tail of the list: @{$vals}[$i+1..$last]= reverse @{$vals}[$i+1..$last] if $vals->[$i+1] gt $vals->[$last]; # Find next item that will make us "greater": my $j= $i+1; $j++ while $vals->[$i] ge $vals->[$j]; # Swap: @{$vals}[$i,$j]= @{$vals}[$j,$i]; return 1; } sub NextPermuteNum(\@) { my( $vals )= @_; my $last= $#{$vals}; return !1 if $last < 1; # Find last item not in reverse-sorted order: my $i= $last-1; $i-- while 0 <= $i && $vals->[$i+1] <= $vals->[$i]; # If complete reverse sort, we are done! if( -1 == $i ) { # Reset to starting/sorted order: @$vals= reverse @$vals; return !1; } # Re-sort the reversely-sorted tail of the list: @{$vals}[$i+1..$last]= reverse @{$vals}[$i+1..$last] if $vals->[$last] < $vals->[$i+1]; # Find next item that will make us "greater": my $j= $i+1; $j++ while $vals->[$j] <= $vals->[$i]; # Swap: @{$vals}[$i,$j]= @{$vals}[$j,$i]; return 1; } sub _NL_Args { my $loops= shift(@_); isa( $loops, "ARRAY" ) or _Croak( "First argument must be an array reference,", " not ", _Type($loops) ); my $n= 1; for my $loop ( @$loops ) { if( ! isa( $loop, "ARRAY" ) && ! isa( $loop, "CODE" ) ) { _Croak( "Invalid type for loop $n specification (", _Type($loop), ")" ); } $n++; } my( $opts )= @_; if( isa( $opts, "HASH" ) ) { shift @_; } else { $opts= {}; } my $code; if( 0 == @_ ) { $code= 0; } elsif( 1 != @_ ) { _Croak( "Too many arguments" ); } else { $code= pop @_; isa($code,"CODE") or _Croak( "Expected CODE reference not ", _Type($code) ); } my $when= delete($opts->{OnlyWhen}) || sub { @_ == @$loops }; if( keys %$opts ) { _Croak( "Unrecognized option(s): ", join ' ', keys %$opts ); } return( $loops, $code, $when ); } sub _NL_Iter { my( $loops, $code, $when )= @_; my @list; my $i= -1; my @idx; my @vals= @$loops; return sub { return } if ! @vals; return sub { while( 1 ) { # Prepare to append one more value: if( $i < $#$loops ) { $idx[++$i]= -1; if( isa( $loops->[$i], 'CODE' ) ) { local( $_ )= $list[-1]; $vals[$i]= $loops->[$i]->(@list); } } ## return if $i < 0; # Increment furthest value, chopping if done there: while( @{$vals[$i]} <= ++$idx[$i] ) { pop @list; return if --$i < 0; } $list[$i]= $vals[$i][$idx[$i]]; my $act; $act= !ref($when) ? $when : do { local( $_ )= $list[-1]; $when->(@list); }; return @list if $act; } }; } sub NestedLoops { my( $loops, $code, $when )= _NL_Args( @_ ); my $iter= _NL_Iter( $loops, $code, $when ); if( ! $code ) { if( ! defined wantarray ) { _Croak( "Useless in void context", " when no code given" ); } return $iter; } my @ret; my @list; while( @list= $iter->() ) { @list= $code->( @list ); if( wantarray ) { push @ret, @list; } else { $ret[0] += @list; } } return wantarray ? @ret : ( $ret[0] || 0 ); } "Filtering should not be straining"; __END__ POD removed from this copy.
I'd like to thank the Sharp Zaurus for making this module possible.
- tyeIn reply to Re: Algorithm::Loops released (code)
by tye
in thread Algorithm::Loops released
by tye
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |