Unfortunately there's no direct built-in that completely flattens a data structure even when sub-lists are wrapped in item containers.
Some possible solutions:
Gather/take
You've already come up with a solution like this, but deepmap
can take care of all the tree iteration logic to simplify it. Its callback is called once for every leaf node of the data structure, so using take
as the callback means that gather
will collect a flat list of the leaf values:
sub reallyflat (+@list) { gather @list.deepmap: *.take }
Custom recursive function
You could use a subroutine like this to recursively slip
lists into their parent:
multi reallyflat (@list) { @list.map: { slip reallyflat $_ } }
multi reallyflat (leaf) { leaf }
Another approach would be to recursively apply <>
to sub-lists to free them of any item containers they're wrapped in, and then call flat
on the result:
sub reallyflat (+@list) {
flat do for @list {
when Iterable { reallyflat $_<> }
default { $_ }
}
}
Multi-dimensional array indexing
The postcircumfix [ ]
operator can be used with a multi-dimensional subscript to get a flat list of leaf nodes up to a certain depth, though unfortunately the "infinite depth" version is not yet implemented:
say @ab[*;*]; # (a (b c) (d) e f [a (b c)] x (y z) w)
say @ab[*;*;*]; # (a b c d e f a (b c) x y z w)
say @ab[*;*;*;*]; # (a b c d e f a b c x y z w)
say @ab[**]; # HyperWhatever in array index not yet implemented. Sorry.
Still, if you know the maximum depth of your data structure this is a viable solution.
Avoiding containerization
The built-in flat
function can flatten a deeply nested lists of lists just fine. The problem is just that it doesn't descend into item containers (Scalar
s). Common sources of unintentional item containers in nested lists are:
An Array
(but not List
) wraps each of its elements in a fresh item container, no matter if it had one before.
- How to avoid: Use Lists of Lists instead of Arrays of Arrays, if you don't need the mutability that Array provides. Binding with
:=
can be used instead of assignment, to store a List
in a @
variable without turning it into an Array
:
my @a := 'a', ('b', 'c' );
my @b := ('d',), 'e', 'f', @a;
say flat @b; # (d e f a b c)
$
variables are item containers.
- How to avoid: When storing a list in a
$
variable and then inserting it as an element into another list, use <>
to decontainerize it. The parent list's container can also be bypassed using |
when passing it to flat
:
my $a = (3, 4, 5);
my $b = (1, 2, $a<>, 6);
say flat |$b; # (1 2 3 4 5 6)
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…