Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

loop in loop method evasion

by motzi (Sexton)
on Dec 26, 2009 at 04:12 UTC ( [id://814392]=perlquestion: print w/replies, xml ) Need Help??

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

I need to generate strings consequentially, i do this with that code (length is 3):
@chartab = (0..9, a..z); foreach $first (@chartab) { foreach $second (@chartab) { foreach $third (@chartab) { print $first, $second, $third, ","; } } }
Here, depending on the length i remove or add more foreach loop. Well, i need for example a 15-length string generation, need to make 15 loops, its a bit hard work.

Is there any way to make this method more compact?

Thanks.

Replies are listed 'Best First'.
Re: loop in loop method evasion
by ikegami (Patriarch) on Dec 26, 2009 at 06:24 UTC
    Feasibility of ever completing $N=15 aside, the following is simple and uses next to no memory:
    use Algorithm::Loops qw( NestedLoops ); my @chartab = (0..9, 'a'..'z'); my $N = 3; NestedLoops( [ (\@chartab) x $N ], sub { print @_, ','; }, );
Re: loop in loop method evasion
by eye (Chaplain) on Dec 26, 2009 at 06:00 UTC
    Another approach to this is to recognize that your sequence of strings can form a one-to-one map with a sequence of integers. You could then use a single loop and a function to map those integers into your strings.

    The trouble is that your task is enormous. You want to generate 36^15 strings. That is well over 2E+23 strings that will require in excess of 1,000,000 exabytes.

    Forget the appearance of your code. You have problems if you want this to finish running this year or if you want to save these strings.

      Another approach to this is to recognize that your sequence of strings can form a one-to-one map with a sequence of integers.

      The advantage of this method is that it uses no memory.

      # XXX Doesn't handle negatives sub idiv { my ($dividend, $divisor) = @_; use integer; my $quotient = $dividend / $divisor; my $remainder = $dividend - ( $quotient * $divisor ); return ( $quotient, $remainder ); } my @chartab = (0..9, 'a'..'z'); my $N = 3; for (0 .. @chartab**$N-1) { my $x = $_; for (1..$N) { ($x, my $i) = idiv($x, 0+@chartab); print $chartab[$i]; } print ','; }
      Well, about 15-character string is only theoretically task, sure i will not use that much, in practice i will only need 5-6 length string max.
Re: loop in loop method evasion
by Anonymous Monk on Dec 26, 2009 at 05:07 UTC
    You actual intent is a little unclear to me. Are you generating permutations with n terms of the list of items in @chartab followed by a comma? If so, recursion would seem the reasonable way to go. Something like (untested):

    use strict; use warnings; my @chartab = (0..9, 'a'..'z'); my $depth = 4; print permute(\@chartab,'',$depth); sub permute { my ($chartab_ref, $intro, $depth) = @_; return "$intro," if ($depth == 0); my $string = q{}; foreach my $letter (@$chartab_ref) { $string .= permute($chartab_ref, $intro.$letter,$depth-1); } return $string; }

      While your algorithm does what the OP wants, it is misnamed. It does not return the permutations of the list. The list only contains one zero, yet the output contains strings with multiple zeroes.

Re: loop in loop method evasion
by biohisham (Priest) on Dec 26, 2009 at 05:10 UTC
Re: loop in loop method evasion
by bobr (Monk) on Dec 26, 2009 at 09:17 UTC
    Or you can use Algorithm::Combinatorics module, specifically variations_with_repetition function. It uses iterator approach, so it consumes quite little memory. As was noted above, the number of results is very large, so following prints just first 50:
    use Algorithm::Combinatorics qw(variations_with_repetition); my $iter = variations_with_repetition([ '0'..'9', 'a'..'z' ], 15); while($seq = $iter->next) { print @$seq,"\n"; last if ++$count > 50; }
    -- Roman
Re: loop in loop method evasion
by bduggan (Pilgrim) on Dec 26, 2009 at 18:17 UTC
    You are counting in base 36. So, you can use Math::Base36, e.g.
    #!/usr/bin/perl -l use Math::Base36 qw/encode_base36/; print lc encode_base36($_,3) for 0..(36**3-1);

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://814392]
Approved by biohisham
Front-paged by biohisham
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (2)
As of 2024-04-19 19:40 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found