Walk on the flats
In #perl6 we are asked on a regular basis how to flatten deep nested lists. The naive approach of calling flat doesn’t do what beginners wish it to do.
my @a = <Peter Paul> Z <70 42> Z <green yellow>; dd @a; # Array @a = [("Peter", IntStr.new(70, "70"), "green"), ("Paul", IntStr.new(42, "42"), "yellow")] dd @a.flat.flat; # ($("Peter", IntStr.new(70, "70"), "green"), $("Paul", IntStr.new(42, "42"), "yellow")).Seq
The reason why @a is so resistant to flattening is that flat
will stop on the first item, what happens to be a list in the example above. We can show that with dd
.
for @a { .&dd } # List @a = $("Peter", IntStr.new(70, "70"), "green") # List @a = $("Paul", IntStr.new(42, "42"), "yellow")
The reason why this easy thing isn’t easy is that you are not meant to do it. Flattening lists may work well with short simple list literals but will become pricey on lazy lists, supplies or 2TB of BIG DATA. Leave the data as it is and use destructuring to untangle nested lists.
If you must iterate in a flattening fashion over your data, use a lazy iterator.
sub descend(Any:D \l){ gather l.deepmap: { .take } } dd descend @a; # ("Peter", IntStr.new(70, "70"), "green", "Paul", IntStr.new(42, "42"), "yellow").Seq
By maintaining the structure of your data you can reuse that structure later, what includes changes in halve a year’s time.
put @a.map({ '<tr>' ~ .&descend.map({"<td>{.Str}</td>"}).join ~ "</tr>" }).join("\n"); # <tr><td>Peter</td><td>70</td><td>green</td></tr> # <tr><td>Paul</td><td>42</td><td>yellow</td></tr>
There are quite a few constructs that will require you to flatten to feed the result to operators and loops. If you concatenate list-alikes like Ranges or Seq returned by metaops, flat
will come in handy.
"BenGoldberg++ for this pretty example".trans( [ flat "A".."Z", "a".."z"] => [ flat "𝓐".."𝓩", "𝓪".."𝔃" ] ).put # OUTPUT«𝓑𝓮𝓷𝓖𝓸𝓵𝓭𝓫𝓮𝓻𝓰++ 𝓯𝓸𝓻 𝓽𝓱𝓲𝓼 𝓹𝓻𝓮𝓽𝓽𝔂 𝓮𝔁𝓪𝓶𝓹𝓵𝓮»
-
August 1, 2016 at 23:112016.31 An End Of An Era | Weekly changes in and around Perl 6