Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle nested array with >> and return a flat array?

Tags:

arrays

raku

I wonder weather there is a concise way in Raku to process a nested array (array of arrays) and flatten the result? When transforming flat arrays >>. is handy, but if I want to return arrays and the result should be flat, what construct is avaiblable in Raku?

grammar g {
    rule port       { <pnamelist> + %% ","}
    token pnamelist { <id> +  }
    token id        { \w }
}

class a {
    method port ($/)      { make  $<pnamelist>>>.made  }
    method pnamelist ($/) { make  $<id>>>.made  }
    method id ($/ )       { make $/.Str }
}

my $r = g.parse("ab,cd", :rule('port'), :actions(a) ).made; 

say $r; # [[a b] [c d]]

The above snippet outputs [[a b] [c d]], however what I actually want is [a b c d] . Is there a concise way to rewrite make $<pnamelist>>>.made so that it iterates over array $<pnamelist> and gathers each elements .made in a flat list what is then input for ``make` ?

like image 341
Konrad Eisele Avatar asked Jul 27 '20 11:07

Konrad Eisele


People also ask

How do you flatten a nested array?

The array method accepts an optional parameter depth , which specifies how deep a nested array structure should be flattened (default to 1 ). So to flatten an array of arbitrary depth, just call flat method with Infinity .


1 Answers

TL;DR Flatten using positional subscripting. For your example, append [*;*] or [**] to $<pnamelist>>>.made. You'll get the same result as Liz's solution.

Why use this solution?

Liz is right that map and relatives (for, deepmap, duckmap, nodemap, and tree) are more flexible, at least collectively, and combining them with .Slip can be just the ticket.

But I often I find it cognitively simpler to use these tools, and others, including hypers, to create whatever data structure, without worrying about .Sliping, and then just flatten the result at the end by just appending [*;*...] or [**] as explained below.

Last but not least, it's simple and succinct:

method port ($/) { make $<pnamelist>>>.made[**] }
                                           ^^^^

Flattening N levels deep with [*;*...]

The general approach for flattening N levels deep works today as @Larry always intended.

The first Whatever strips away the outer array; each additional Whatever, separated by a ;, peels away another inner level of nesting. For your current example two Whatevers does the job:

method port ($/) { make $<pnamelist>>>.made[*;*] }
                                           ^^^^^

This yields the same result as Liz's solution:

(a b c d)

If you want the end result to have an outer array, just add it to the end result wherever you think appropriate, eg:

method port ($/) { make [ $<pnamelist>>>.made[**] ] }

Bulldozing with [**]

If you want to bulldoze a nested array/list, peeling away all nesting no matter how deep, you could just write more *;s than you can possibly need. Any extras won't make any difference.

But the desire to bulldoze is natural enough, and comes up often enough, that it makes sense to have an operation that does it without needing a hacky notion like "just write lots of *;".

So it should come as no surprise that @Larry specified such a bulldozing operation a decade or so ago. It's nicely consistent with the rest of Raku's feel, using a HyperWhatever (**) as an indexing value.

But trying it:

method port ($/) { make $<pnamelist>>>.made[**] }
                                           ^^^^

currently yields:

HyperWhatever in array index not yet implemented. Sorry.

Fortunately one can very easily "fake" it:

sub postfix:< [**] > (\lhs) { gather lhs.deepmap: *.take }

The body of the postfix comes from here.

With this in place then changing the [*;*] to [**] will work for your example but will continue to work no matter how deeply nested its left hand side is.

And presuming HyperWhatever in array index is one day implemented as a built in, one will be able to remove the postfix definition and any code using it will work without it -- and, presumably, get a speedup.

like image 121
raiph Avatar answered Sep 26 '22 17:09

raiph