19

Given the word abcd, how would I construct the following nested list?

[ (abcd) (a bcd) (ab cd) (abc d) (a b cd) (a bc d) (ab c d) (a b c d) ]

That is splitting the word in every way it can be, while keeping the letters in order.

Nemokosch on #raku-beginner pointed me to .combinations and the module snip, but I'm having trouble putting it all together.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jim Bollinger
  • 1,671
  • 1
  • 13

2 Answers2

18

You could use match with :exhaustive:

"abcd"
andthen .match: /^ (\w+)+ $/,:ex
andthen .map: *.[0].put
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
wamba
  • 3,883
  • 15
  • 21
14
use v6.e.PREVIEW;  # for `snip`

my $s  = "abcd";
my @ps = $s.comb.pairs;

my $res = (1..^$s.chars
              ==> combinations()
              ==> map({ @ps.snip: (&{.key < $OUTER::_} for $_) })
              ==> map( *>>.value>>.join ));
  • get all the combinations of the enumeration of the string (except 0th) to determine the split positions
  • then snip the enumeration-decorated string over those positions while generating the condition blocks on the fly
  • and lastly undecorate the enumeration and join the splitted values

to get

>>> $res
((abcd) (a bcd) (ab cd) (abc d) (a b cd) (a bc d) (ab c d) (a b c d))

[other answer++ is more concise but as I understand it, it's not lazy and performs somewhat slower on larger strings, e.g., "abcd" x 5, so i'm keeping this answer.]


here are the intermediate results:

# enumerated string
>>> @ps = $s.comb.pairs
[0 => a 1 => b 2 => c 3 => d]

# combinations of the enumeration except 0s
# these are the splitting points actually
>>> $combs = (1..^$s.chars).combinations
(() (1) (2) (3) (1 2) (1 3) (2 3) (1 2 3))

Out of these splitting points, we can generate, e.g., (*.key < 1,) and (*.key < 1, *.key < 3) lists so that we can pass these to @ps.snip which will then look at the keys of its elements, i.e., index positions, and split over them for us. So the next step does this generation:

# output is actually different but this is for demonstration
>>> $combs.map({ (&{.key < $OUTER::_} for $_) })
((), (*.key < 1), (*.key < 2), (*.key < 3), (*.key < 1, *.key < 2) ... (*.key < 1, *.key < 2, *.key < 3)

(The first empty "condition" is handy to produce the string as is later, so we don't deal with that case separately.)

For each combination, e.g., (1, 2), we iterate over this (for $_) and generate the conditions. 1 and 2 will in turn be $_ in that post-form for loop, but if we had put {.key < $_} as a condition, the $_ won't close over those 1 and 2 but instead will be the argument passed at calling time, i.e., an enumerated pair. But we don't want that, so we say to put here the OUTER block's $_, which will be 1 and 2 in turn. But for .key, we do want the pair at calling time, so it's $_.key (without the $_ as it can be omitted.)

At this point after snips we have

>>> $combs.map({ @ps.snip: (&{.key < $OUTER::_} for $_) }).raku
(((0 => "a", 1 => "b", 2 => "c", 3 => "d"),).Seq, ((0 => "a",), (1 => "b", 2 => "c", 3 => "d")).Seq, ((0 => "a", 1 => "b"), (2 => "c", 3 => "d")).Seq, ((0 => "a", 1 => "b", 2 => "c"), (3 => "d",)).Seq, ((0 => "a",), (1 => "b",), (2 => "c", 3 => "d")).Seq...)

We have the desired splits except there are extra baggages: the enumerations and the splitting to chars. So we undo the .comb.pairs done at the beginning with >>.value>>.join at the end, and the final result follows.

Mustafa Aydın
  • 17,645
  • 4
  • 15
  • 38