关于perl6:我怎样才能完全展平Perl 6列表(列表(列表)…)

How can I completely flatten a Perl 6 list (of lists (of lists) … )

我想知道我如何才能完全扁平化列表和包含它们的东西。除此之外,我还提出了一个解决方案,它可以将具有多个元素的东西放回原处,或者在将其放回原处后再将具有一个元素的东西带走。

这与我如何"扁平化"Perl6中的列表有点不同?这并不是完全平坦的,因为任务是重组。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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;

这就产生了:

1
2
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的答案:

  • 在python中从列表中创建一个简单列表
  • 如何将列表扁平化一步
  • 将列表列表展平为列表列表

itertools.chain(*sublist)看起来很有趣,但答案要么是递归的,要么局限于硬编码的两个层次。函数语言在源代码中是递归的,但我希望如此。


不幸的是,即使子列表包装在项容器中,也没有直接内置的可以完全扁平化数据结构。

一些可能的解决方案:

收集/带走

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

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

。自定义递归函数

您可以使用这样的子例程递归地将cx1〔4〕列表导入其父列表:

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

另一种方法是递归地将<>应用于子列表,以释放它们所包装的任何项容器,然后对结果调用flat

1
2
3
4
5
6
sub reallyflat (+@list) {
    flat do for @list {
        when Iterable { reallyflat $_<> }
        default       { $_               }
    }
}

。多维数组索引

postcircumfix [ ]运算符可以与多维下标一起使用,以获得某个深度的叶节点平面列表,但不幸的是,"无限深度"版本尚未实现:

1
2
3
4
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.

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

避免集装箱化

内置的flat函数可以扁平一个嵌套很深的列表列表。问题是它不会下降到物品容器(Scalars)。嵌套列表中意外项容器的常见来源是:

  • 一个Array(而不是List)将它的每个元素包装在一个新的物品容器中,不管它以前是否有一个。

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

  • $变量是项目容器。

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


我不知道这样做的内置方法,尽管很可能有(如果没有,可能应该有)。

我能在短时间内想出的最好办法是:

1
gather @ab.deepmap(*.take)

我不确定collect/take如何与超运算符的潜在并行评估进行交互,因此以下替代方法可能不安全,尤其是在您关心元素顺序的情况下:

1
gather @ab>>.take

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

最后,这是第一个重新布线为复古样式子例程的解决方案:

1
sub deepflat { gather deepmap &take, @_ }