views:

83

answers:

3

For example, I have an array

my @arr = qw(0 1 2 3 4);

How do I get the following combinations:

0
01
012
0123
01234
1
12
123
1234
2
23
234
3
34
4

If any, what's the name for this kind of combination (or permutation)?

Thanks like always!

+6  A: 

Use array slices:

#! /usr/bin/perl

use warnings;
use strict;

my @arr = qw(0 1 2 3 4);

my @result;
for (my $i = 0; $i < @arr; $i++) {
  for (my $j = $i; $j < @arr; $j++) {
    push @result => [ @arr[$i .. $j] ];
  }
}

print @$_, "\n" for @result;

Output:

0
01
012
0123
01234
1
12
123
1234
2
23
234
3
34
4
Greg Bacon
@gbacon, thanks for the code :) It really helps me to improve my Perl skills. BTW, when we create an anonymous array using array slices of @arr as its contents and then push its reference to @result, why there's the fat comma "=>", instead of normal comma ","? Is this a convention? I've only seen the fat comma used in key/value pairs and the use constant subroutine.
Mike
Using `=>` outside of key/value pairs is a style enjoyed by some people. I find it kinda wacky, though.
friedo
@Mike In Perl 5 the fat comma is mostly just a fancy looking comma operator (it does stringify barewords on its left though). Using it in cases other than hash constructors occasionally makes the code more readable. For example, [`Moose`](http://search.cpan.org/dist/Moose-1.12/lib/Moose.pm)'s `has x => (isa => "Int", is => "rw");` is easier to read than `has "x", "isa", "Int", "is", "rw";`; however, in this case I think it makes the code harder to read. It makes it look like `@result` is going to be pushed onto the anonymous arrayref.
Chas. Owens
@friedo and @Chas.Owens, thank you for the explanation of the fat comma. I understand now. Thanks :)
Mike
+6  A: 

Personally I find the "C style" for loop that gbacon uses often complicates code unnecessarily. And it's usually possible to replace it with the "range-style" for loop that is easier to follow.

#!/usr/bin/perl

use strict;
use warnings;

my @arr = qw(0 1 2 3 4);

my @result;
for my $i (0 .. $#arr) {
  for my $j ($i .. $#arr) {
    push @result => [ @arr[$i .. $j] ];
  }
}

print @$_, "\n" for @result;
davorg
@davorg, thanks for sharing the code :) I agree this "range-style" for loop is more concise and more readable. Thanks :)
Mike
+1  A: 

Here's a way to divide up the problem into more discrete components:

use strict;
use warnings;

sub consec_subseq_leading {
    # (1, 2, 3) ==> ( [1], [1, 2], [1, 2, 3] )
    return map [ @_[0 .. $_] ], 0 .. $#_;
}

sub consec_subseq {
    # (1, 2, 3) ==> ( F(1, 2, 3), F(2, 3), F(3) )
    # where F = consec_subseq_leading
    my $j = $#_;
    return map consec_subseq_leading( @_[$_ .. $j] ), 0 .. $j;
}

my @cs = consec_subseq(0 .. 4);
print "@$_\n" for @cs;
FM
@FM, thanks for sharing the code that has the comments :)
Mike