我怎样才能完全扁平化列表(列表(列表)...)

How can I completely flatten a list (of lists (of lists) ... )

我想知道如何完全扁平化列表和包含它们的东西。除其他事项外,我想出了这个解决方案,可以将具有多个元素的东西放回原位,或者在将其放入一个元素后将其放回原处。

这与 有点不同,后者并不完全平坦,因为任务是重组。

但是,也许有更好的方法。

my @a  = 'a', ('b', 'c' );
my @b  = ('d',), 'e', 'f', @a;
my @c  = 'x', $( 'y', 'z' ), 'w';

my @ab = @a, @b, @c;
say "ab: ", @ab;

my @f = @ab;

@f = gather {
    while @f {
        @f[0].elems == 1 ??
            take @f.shift.Slip
                !!
            @f.unshift( @f.shift.Slip )
        }
    }

say "f: ", @f;

这给出:

ab: [[a (b c)] [(d) e f [a (b c)]] [x (y z) w]]
f: [a b c d e f a b c x y z w]

奇怪的是,我还阅读了一些 python 答案:

itertools.chain(*sublist) 看起来很有趣,但答案要么是递归的,要么仅限于硬编码的两个级别。函数式语言在源代码中是递归的,但我预料到了。

我不知道 built-in 这样做的方法,虽然很可能有(如果没有,可能应该有)。

我能在短时间内想到的最好的是:

gather @ab.deepmap(*.take)

我不确定 gather/take 如何与超级运算符的潜在并行计算交互,因此使用以下替代方法可能不安全,特别是如果您关心元素顺序:

gather @ab>>.take

如果需要数组,可以将代码放在方括号中,或者通过.list将其具体化为列表。

最后,这是第一个重写为 retro-style 子例程的解决方案:

sub deepflat { gather deepmap &take, @_ }

不幸的是,没有直接的 built-in 可以完全扁平化数据结构,即使 sub-lists 被包裹在项目容器中也是如此。

一些可能的解决方案:

Gather/take

您已经提出了这样的解决方案,但是 deepmap 可以处理所有的树迭代逻辑来简化它。它的回调对数据结构的每个叶节点调用一次,因此使用 take 作为回调意味着 gather 将收集叶值的平面列表:

sub reallyflat (+@list) { gather @list.deepmap: *.take }

自定义递归函数

您可以使用这样的子例程递归 slip 列表到它们的父级:

multi reallyflat (@list) { @list.map: { slip reallyflat $_ } }
multi reallyflat (\leaf) { leaf }

另一种方法是递归地将 <> 应用到 sub-lists 以释放它们包装在其中的任何物品容器,然后在结果上调用 flat

sub reallyflat (+@list) {
    flat do for @list {
        when Iterable { reallyflat $_<> }
        default       { $_               }
    }
}

Multi-dimensional数组索引

postcircumfix [ ] 运算符可以与 multi-dimensional 下标一起使用以获得达到一定深度的叶节点的平面列表,但不幸的是 "infinite depth" 版本尚未实现:

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.

不过,如果您知道数据结构的最大深度,这是一个可行的解决方案。

避免容器化

built-in flat 函数可以很好地展平深度嵌套的列表列表。问题只是它没有进入物品容器 (Scalars)。嵌套列表中意外项目容器的常见来源是:

  • 一个 Array(但不是 List)将其每个元素包装在一个新的项目容器中,无论它之前是否有一个。

    • 如何避免: 如果您不需要数组提供的可变性,请使用列表的列表而不是数组的数组。可以使用 := 绑定而不是赋值,将 List 存储在 @ 变量中而不将其转换为 Array:
      my @a  := 'a', ('b', 'c' );
      my @b  := ('d',), 'e', 'f', @a;
      say flat @b; # (d e f a b c)
  • $ 变量是物品容器。

    • 如何避免: 将列表存储在 $ 变量中,然后将其作为元素插入到另一个列表中时,使用 <> 去容器化它。将父列表的容器传递给 flat 时,也可以使用 | 绕过它:
      my $a = (3, 4, 5);
      my $b = (1, 2, $a<>, 6);
      say flat |$b; # (1 2 3 4 5 6)