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

Hi,
This question is for understanding rather than getting it to work.
Let say I want to simply generate all telephone numbers (of length 7).
Update: or else, all words of length X given an alphabet of Y symbols...
#!/usr/bin/perl -w use strict; for my $i (0..9) { for my $j (0..9) { for my $k (0..9) { for my $l (0..9) { for my $m (0..9) { for my $n (0..9) { for my $o (0..9) { print $i,$j,$k,$l,$m,$n,$o,"\n"; } } } } } } }
By using Algorithm::Loops:
#!/usr/bin/perl -w use strict; use Algorithm::Loops qw( NestedLoops ); my @symbol = qw(0 1 2 3 4 5 6 7 8 9); my @position; for my $deepness (0..6) { push @position, [@symbol]; } my @list; my $iter = NestedLoops(\@position); while (@list = $iter->()) { print "@list\n"; }
I would appreciate very much if one could explain in detail how this Nested Loop works ? I tried many times to complete the code below from scratch and getting nowhere.
my @symbol=qw(0 1 2 3 4 5 6 7 8 9); my$deepness=7; #? Repeat the foreach to specified deepness ?
Thanks, regards.

Replies are listed 'Best First'.
Re: Telephone - Nested Loops
by dynamo (Chaplain) on Jun 22, 2007 at 14:16 UTC
    Note: I have never used NestedLoops before reading this question, so take this with a grain of salt.

    Having said that, and having read through the docs a bit, NestedLoops lets you do with a single loop what you'd otherwise use a chain of N foreach loops to do, i.e. collectively walk across an N-dimensional parameter space.

    In your phone number example, N=7, and each dimension would be a digit in the whole number. To keep it simple I'm going to use a 3 digit number (N=3) instead.

    So, instead of writing:

    @A_options = @B_options = @C_options = qw(0 1 2 3 4 5 6 7 8 9); foreach my $A (@A_options) { foreach my $B (@B_options) { foreach my $C (@C_options) { print $A . $B . $C . ;\n"; } } }
    you could write:
    @A_options = @B_options = @C_options = qw(0 1 2 3 4 5 6 7 8 9); NestedLoops( [\@A_options,\@B_options,\@C_options], sub {print $_[0] . $_[1] . $_[2] . "\n";} );
    The information about where you are in the N-dimensional loop is passed to the called subroutine as consecutive params under @_.

    Good luck,
    - Dynamo

Re: Telephone - Nested Loops
by ozone (Friar) on Jun 22, 2007 at 15:14 UTC
    sounds like a great candidate for recursion:

    getPhoneNumbers(7); sub getPhoneNumbers { my $maxLength = shift; my $curPhoneNumber = shift || '0'; if($maxLength - 1) { for(my $i = 0; $i < 10; $i++) { getPhoneNumbers($maxLength - 1, $curPhoneNumber.$i); } } else { print $curPhoneNumber, "\n"; } }
Re: Telephone - Nested Loops
by Moron (Curate) on Jun 22, 2007 at 13:37 UTC
    NestedLoops takes a structure over which it will iterate but it needs a second parameter to tell it what to do per iteration. So you could do your thing by passing it a 7- deep nested array by reference and a code ref like sub { print "$_\n" for @_; }; But as a rule, such predictable structures should never even be built (update: I have a habit of not building ranges like 0..7 even though that's small, because I don;t want to build in memory a 0..colussus by accident!). So the thing should never even get the chance to be passed to a method; rather the algorithm should be a bit smarter.

    # updated to add leading zeroes sub Arbitel { # takes number of digits as argument my $count = shift; my $limit = 10**$count; for ( my $i = 0; $i < $limit; $i++ ) { print Lzro( $count, $i ) . "\n"; } } sub Lzro my ( $count, $number ) = @_; substr( ( '0' x $count ) . $number, -$count ); }
    More update: my decision chart:

    "Can it be mapped to a simple virtual structure? Y: don't build it and don;t bother to use a CPAN module to generate or iterate it.

    N: "Can the structure be iterated virtually without building it? N: use Algorithm::

    Y: "Is there a method for it in Math::Combinatorics? Y: use that then N: use Algorithm::

    __________________________________________________________________________________

    ^M Free your mind!

      NestedLoops takes a structure over which it will iterate [...]. So you could do your thing by passing it a 7- deep nested array by reference and a code ref like sub { print "$_\n" for @_; };

      Perhaps you should (re-)read the documentation for Algorithm::Loops::NestedLoops(), as you appear to misunderstand what it does, or at least your attempt to re-explain what it does is just very badly done. To start with the easy mistake: You don't have to pass code to NestedLoops() as it is happy to just give you an iterator.

      What you give to NestedLoops() is not 7-deep. It doesn't "iterate over a structure". Your description makes it sound like NestedLoops() just traverses a data structure similar to what Data::Dumper does, that it is (to borrow bad terminology from a bad "design pattern") a "visitor pattern" implementation.

      You give NestLoops() a reference to a list of ranges. So you might call this a 2-deep data structure but that'd really miss the point. It is a list of ranges and each range can just be a reference to a list but could also be something else. It doesn't iterate over this data structure. It iterates over the space of all possible ordered combinations of items from these ranges (and does so in the order most easily described as that resulting from nesting loops that each iterate over one range).

      - tye        

Re: Telephone - Nested Loops
by Limbic~Region (Chancellor) on Jun 22, 2007 at 15:04 UTC
Re: Telephone - Nested Loops
by Ploux (Acolyte) on Jun 22, 2007 at 13:37 UTC

      In addition to the source code, Coming soon: Algorithm::Loops is where I first wrote that source code. Although, looking over the thread, there isn't much discussion of that code. But there is some as well as links to related discussions.

      - tye        

Re: Telephone - Nested Loops
by jdporter (Paladin) on Jun 22, 2007 at 18:06 UTC

    Not an answer to your question, but a solution to your need, maybe...

    # arg 0: callback; is passed each generated vector # arg 1: length of generated vectors # remaining args: alphabet sub generate_all_vectors(&$@) { my( $callback, $veclen, @alphabet ) = @_; for ( my $i = 0; $i < @alphabet ** $veclen; $i++ ) { my $t = $i; # destructible copy $callback->( reverse map { my $n = $t % @alphabet; $t = int( $t / @alphabet ); $alphabet[$n] } 1 .. $veclen ) } } generate_all_vectors { print "@_\n" } 7, 0 .. 9;
    A word spoken in Mind will reach its own level, in the objective world, by its own weight