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

Hi,

For a arbitrary length string (I know the length at runtime), what loop will give me the following series of numbers?

say for $length =4

1111 1112 1113 1121 1122 1123 1131 1132 .. 3111 3112 3113 .. 3333

For a given $length, I can do this using $length nested for loops, but I can't work out how to do it for any length string.

Thanks for your wisdom.

SD

Replies are listed 'Best First'.
Re: Generating lists of strings
by gmargo (Hermit) on Jan 24, 2010 at 16:57 UTC

    Here's a method using Algorithm::Combinatorics.

    use Algorithm::Combinatorics qw(variations_with_repetition); my $length = 4; my @data = qw(1 2 3); my $iter = variations_with_repetition(\@data, $length); while (my $p = $iter->next) { print join("",@$p)."\n"; }
Re: Generating lists of strings
by jethro (Monsignor) on Jan 24, 2010 at 15:38 UTC

    Looks like you are counting base 3, but with every digit plus 1, i.e. 0000 becomes 1111 and 2222 becomes 3333

    #!/usr/bin/perl use warnings; use strict; my $length=4; my @number= (1) x $length; while (@number) { printnumber(@number); @number= addto(@number); } sub addto { my @number= @_; while (1) { my $carry=1; foreach (@number) { $_+= $carry; $carry=0; if ($_>3) { $_=1; $carry=1; } } @number=() if ($carry==1); return(@number); } } sub printnumber { my @number= @_; print reverse(@number),"\n"; }

    Your problem is a bit underspecified in that it is not clear if $length should also be a limit to the digits you use. I assumed not.

Re: Generating lists of strings
by BrowserUk (Patriarch) on Jan 24, 2010 at 17:11 UTC

    #! perl -slw use strict; sub nFor(&@) { my $code = shift; die "First argument must be a code ref" unless ref( $code ) eq 'CO +DE'; my @limits = @_; my @indices = ( 0 ) x @limits; for( my $i = $#limits; $i >= 0; ) { $i = $#limits; $code->( @indices ), ++$indices[ $i ] while $indices[ $i ] < $limits[ $i ]; $i = $#limits; $indices[ $i ] = 0, ++$indices[ --$i ] while $i >= 0 and $indices[ $i ] == $limits[ $i ]; } } my @digits = 1 .. 3; nFor { print join '', @digits[ @_ ]; } ( 3 ) x 4;

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

      That looks like a lot like the implementation of NestedLoop. Might as well use it.

      use Algorithm::Loops qw( NestedLoops ); my @digits = (1..3); NestedLoops( [ (\@digits) x 4 ], sub { print( @_, "\n" ); }, );

      Update: In addition to the above, this node originally said that BrowerUk's output was wrong and had code to fix the problem, but I was looking at a reply that produced incorrect output, not the OP. The incorrect version was removed.

        NestedLoop. Might as well use it.

        Your choice for your code. I much prefer the far simpler syntax of nFor.

        And, I think that the mirror between the syntax of nFor and the built-ins map & grep, makes my version far more familiar and digestable (though much less flexible), than the tortuous documentation for NestedLoops which I've still never wrapped by brain around.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Generating lists of strings
by Anonymous Monk on Jan 24, 2010 at 15:27 UTC
    use strict; use warnings; use Math::NumberBase; my $length = 4; my $b = Math::NumberBase->new($length); my $start = $b->to_decimal("1" x $length); my $end = $b->to_decimal("${\($length-1)}" x $length); print $b->from_decimal($_), "\n" for $start .. $end; __END__ 1111 1112 1113 1120 1121 1122 1123 1130 1131 1132 1133 1200 1201 ... 3310 3311 3312 3313 3320 3321 3322 3323 3330 3331 3332 3333
      And in the spirit of jethro's analysis, here's a simpler method:
      my $length = 4; my $b = Math::NumberBase->new($length-1); my $start = 0; my $end = $b->to_decimal("${\($length-2)}" x $length); print "$_\n" for map {join'', map {$_+1} split(//,$b->from_decimal($_))} $start .. $end;
      Sorry, I missed that you have no ending zeros. This change recreates the desired output:
      print $b->from_decimal($_), "\n" for grep { $_ % $length } $start .. $end;
Re: Generating lists of strings
by johngg (Canon) on Jan 24, 2010 at 22:31 UTC

    Perhaps you could use glob.

    $ perl -E ' > @digits = ( 1 .. 3 ); > $length = 4; > $globStr = qq{{@{ [ join q{,}, @digits ] }}} x $length; > say for glob $globStr;' 1111 1112 1113 1121 1122 1123 1131 1132 1133 1211 1212 1213 1221 1222 1223 1231 1232 1233 1311 1312 1313 1321 1322 1323 1331 1332 1333 2111 2112 2113 2121 2122 2123 2131 2132 2133 2211 2212 2213 2221 2222 2223 2231 2232 2233 2311 2312 2313 2321 2322 2323 2331 2332 2333 3111 3112 3113 3121 3122 3123 3131 3132 3133 3211 3212 3213 3221 3222 3223 3231 3232 3233 3311 3312 3313 3321 3322 3323 3331 3332 3333 $

    I hope this is useful.

    Cheers,

    JohnGG