12

Let's say I want all permutations of 2 letters out of a, b and c.

I can do:

my @perm = <a b c>.combinations(2)».permutations;
say @perm;
# [((a b) (b a)) ((a c) (c a)) ((b c) (c b))]

which is close, but not exactly what I need. How do I “flatten” this so that I get:

# [(a b) (b a) (a c) (c a) (b c) (c b)]

?

Pat
  • 36,282
  • 18
  • 72
  • 87
mscha
  • 6,509
  • 3
  • 24
  • 40
  • This looks like a duplicate of http://stackoverflow.com/questions/5166662/perl-what-is-the-easiest-way-to-flatten-a-multidimensional-array – FuePi Jun 03 '16 at 13:27
  • 7
    @FuePi, not at all. Perl 5 ≠ Perl 6. – mscha Jun 04 '16 at 20:39

4 Answers4

14

See also "a better way to accomplish what I (OP) wanted".

See also "Some possible solutions" answer to "How can I completely flatten a Raku list (of lists (of lists) … )" question.

Add a subscript

my \perm = <a b c>.combinations(2)».permutations;
say perm;       # (((a b) (b a)) ((a c) (c a)) ((b c) (c b)))
say perm[*];    # (((a b) (b a)) ((a c) (c a)) ((b c) (c b)))
say perm[*;*];  # ((a b) (b a) (a c) (c a) (b c) (c b))
say perm[*;*;*] # (a b b a a c c a b c c b)

Notes

I used a non-sigil'd variable because I think it's a bit clearer what's going on for those who don't know Raku.

I didn't append the subscript to the original expression but I could have:

my \perm = <a b c>.combinations(2)».permutations[*;*];
say perm;       # ((a b) (b a) (a c) (c a) (b c) (c b))
raiph
  • 31,607
  • 3
  • 62
  • 111
  • I like the `[*;*]` trick to “flatten” the list of lists! I don't like the sigil-less variable, though, which is a lot *less* clear for those of us who *do* know Perl (5 or 6). – mscha May 16 '16 at 22:36
  • @Larry's [speculation/specification of multidimensional arrays](http://design.perl6.org/S09.html#Multidimensional_arrays) makes interesting reading. Some of the simpler parts of it like this "trick" have already been implemented in v6.c and the Rakudo Perl 6 compiler. – raiph May 17 '16 at 01:50
10

Ultimately, you are building your list the wrong way to begin with. You can slip your permutations into the outer list like this.

<a b c>.combinations(2).map(|*.permutations);

Which yields the following list

((a b) (b a) (a c) (c a) (b c) (c b)) 

According to the Bench module, this is about 300% faster than doing

<a b c>.combinations(2).map(*.permutations)[*;*]
elcaro
  • 2,227
  • 13
  • 15
  • 1
    Thanks, this is indeed a better way to accomplish what I wanted. I'm not marking this as the best answer, though, because it isn't strictly an answer to the question I asked. :) – mscha Aug 19 '16 at 14:32
9

By inserting slips as appropriate, eg via

<a b c>.combinations(2).map(*.permutations.Slip).Array

or

[ slip .permutations for <a b c>.combinations(2) ]

Invoking .Array in the first example is unnecessary if you're fine with a Seq, and can be replaced with calls to .list or .cache (supplied by PositionalBindFailover) if mutability is not needed.

In the second example, the prefix | operator could be used instead of the slip sub.

Christoph
  • 164,997
  • 36
  • 182
  • 240
  • `».` and `map` are not equivalent. The former is a candidate for auto-threading. Creating a LoL with one less level may not be what he wants. –  May 11 '16 at 23:41
  • @gfldex: you can always throw in a `.hyper` call if that's what you want; also note that in your example, the list of combinations gets iterated twice – Christoph May 12 '16 at 08:39
  • Thanks, `.Slip` is the trick I needed. Too bad that `».Slip` doesn't seem to work... – mscha May 12 '16 at 10:22
  • @mscha: hyperoperators descend into nested structures, so you're slipping the single-element leaves, which is a noop – Christoph May 12 '16 at 14:35
  • @Christoph, I don't think that is true, otherwise the `».permutations` part wouldn't have worked either. – mscha May 12 '16 at 21:13
  • @mscha: that's because the `permutations` method is annotated with the `nodal` trait (cf `Any.pm`, `List.pm`), which prevents hyper operators from performing a deep map (cf `metaops.pm`, look for `METAOP_HYPER_POSTFIX`); not sure if this is explained in the user documentation, but it's mentioned [in the design docs](http://design.perl6.org/S03.html#Unary_hyper_operators) – Christoph May 12 '16 at 21:38
5
my @perm = <a b c>.combinations(2)».permutations;
dd [ @perm.map(*.Slip) ]
# OUTPUT«[("a", "b"), ("b", "a"), ("a", "c"), ("c", "a"), ("b", "c"), ("c", "b")]␤»

However, you may be better of to destructure the LoL when you use it later in the program. A map on a long list can take a jolly long time.