Home > Raku > Recursive Cinderella

## Recursive Cinderella

PWC 184 asked to split an array into numbers and letters.

``````for ( 'a 1 2 b 0', '3 c 4 d'), ( '1 2', 'p q r', 's 3', '4 5 t') -> @a is copy {
@a.=map: *.split(' ').cache;
my @numbers = @a.deepmap({ / <[0..9]> / ?? .Numeric !! Empty }).grep(+*)».Array;
my @letters = @a.deepmap({ / <[a..z]> / ?? .Str !! Empty }).grep(+*)».Array;
say @numbers, ‘ and ’, @letters;
}``````

I use `deepmap` with `Empty` to separate to weed from the chaff and remove the extra structure added by `deepmap` with a `grep`. That extra structure raised the question, what would be needed to split a tree in twain. This could be useful to gain parts of a XML Document with a single parser pass, while maintaining the structure of the tree. Relation of child nodes and parent nodes often carries meaning.

``````for ( 'a 1 2 b 0', '3 c 4 d'), ( '1 2', 'p q r', 's 3', '4 5 t'), [ '1', [ [ 'a' ], '2 b'], '3 c' ] -> @a is copy {

multi sub cinderella(@data, @a, \$needle-a, @b, \$needle-b) {
for @data {
@a[\$++] := my \$new-a;
@b[\$++] := my \$new-b;
cinderella(\$_, \$new-a, \$needle-a, \$new-b, \$needle-b)
}
}

multi sub cinderella(@data, Any:U \$a is raw, \$needle-a, Any:U \$b is raw, \$needle-b) {
my @new-a;
my @new-b;
for @data {
cinderella(\$_, \$a, \$needle-a, \$b, \$needle-b);
@new-a.push: \$a;
@new-b.push: \$b;
}
\$a = @new-a;
\$b = @new-b;
}

multi sub cinderella(Str:D \$s, \$a is raw, \$needle-a, \$b is raw, \$needle-b) {
cinderella(\$_, my @new-a, \$needle-a, my @new-b, \$needle-b) for \$s.split(' ');
\$a = @new-a ?? @new-a.join(' ') !! Empty;
\$b = @new-b ?? @new-b.join(' ') !! Empty;
}

multi sub cinderella(Str:D \$s where *.chars == 1, @a, \$needle-a, @b, \$needle-b) {
given \$s {
when \$needle-a { @a.push: \$s }
when \$needle-b { @b.push: \$s }
default { fail('dunno where to put: ' ~ \$s) }
}
}

cinderella @a, my @numbers, / <[0..9]> /, my @letters, / <[a..z]> /;

dd @numbers, @letters;
}

# OUTPUT: Array @numbers = ["1 2 0", "3 4"]
Array @letters = ["a b", "c d"]
Array @numbers = ["1 2", Empty, "3", "4 5"]
Array @letters = [Empty, "p q r", "s", "t"]
Array @numbers = ["1", [[[],], "2"], "3"]
Array @letters = [[], [["a"], "b"], "c"]``````

The leaves in the target tree can be either a `Str` or (empty) `Array`. However, the first multi candidate doesn’t make the decision what item is added to the target `Array`s. Instead I create a fresh scalar container and bind it to a container within the `Array`s. I then pass that container on to the next `multi`-candidate where it might be filled with `Positional`– or `Str`-object. Please note the distinction. MMD doesn’t care about the container type we use, it looks for the properties of the value. This allows me to split the strings on a white space and pass it on into the next round of MMD matches. The 2nd candidate handles the case where we descent into a nested `Array`. It can manipulate the scalar container created with `@a[\$++] := my \$new-a;` and turn it into a Positional value (here an `Array`), because that initial container is passed into the multi with `is raw`.

This is a very powerful concept. Writing the same with a single recursive function would contain a lot of special casing and be no joy to debug.

Not doing what the instructor asked for seems to produce better results. I shall do so more often.