我怎样才能完全展平一个列表(列表(列表)......)

Posted

技术标签:

【中文标题】我怎样才能完全展平一个列表(列表(列表)......)【英文标题】:How can I completely flatten a list (of lists (of lists) ... ) 【发布时间】:2021-08-03 12:22:31 【问题描述】:

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

这与How do I “flatten” a list of lists in perl 6? 有点不同,后者并不完全平坦,因为任务是重组。

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

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 答案:

Making a flat list out of list of lists in Python How flatten a list of lists one step flatten list of lists of lists to a list of lists

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

【问题讨论】:

【参考方案1】:

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

一些可能的解决方案:

收集/采取

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

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

自定义递归函数

您可以使用这样的子例程以递归方式将 slip 列表添加到它们的父级中:

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

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

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

多维数组索引

postcircumfix [ ] 运算符可以与多维下标一起使用,以获取到一定深度的叶节点的平面列表,但遗憾的是“无限深度”版本尚未实现:

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)将其每个元素包装在一个新的项目容器中,无论它之前是否有一个。

如何避免: 如果您不需要 Array 提供的可变性,请使用 Lists of Lists 而不是 Arrays of Arrays。可以使用与:= 绑定来代替赋值,将List 存储在@ 变量中,而不会将其转换为Array:
我的@a := 'a', ('b', 'c' );
我的@b := ('d',), 'e', 'f', @a;
说平@b; # (d e f a b c)

$ 变量是物品容器。

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

【讨论】:

是否有机会将这样的deepflat 函数(或运算符,我指定|| 它还没有被使用)添加到 6.d? 我有点喜欢使用没有大括号/括号的现有内置插件:say gather @ab.deepmap: *.take;. @raiph:看起来确实更好。已更新。 @mscha speculations 建议拉里的意图是保留前缀: 以“在分号级别将列表插入分号列表”,并且 deepflat 的预期解决方案是 @987654349 @下标。 深度图 FTW。至于“避免容器”,有时你不得不拿别人给你的东西。最好的解决方案可以解决所有问题。【参考方案2】:

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

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

gather @ab.deepmap(*.take)

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

gather @ab>>.take

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

最后,这是第一个被改写为复古风格子程序的解决方案:

sub deepflat  gather deepmap &take, @_ 

【讨论】:

嘿,我没有看到你已经想出了相同的 deepmap 解决方案,而我正在写我的答案......是的,一旦 » 学会像设计文档一样并行化自己预期,您的第二个解决方案很可能不会以正确的顺序返回元素。 [x] 移除了迷信的大括号(并添加了 deepflat 实现)

以上是关于我怎样才能完全展平一个列表(列表(列表)......)的主要内容,如果未能解决你的问题,请参考以下文章

是否有展平嵌套元素列表的功能?

如何将嵌套列表的两个维度完全展平?

如何在 Python 中完全展平列表? [复制]

Pandas DataFrame 中的嵌套字典列表

展平列表列表

用一个语句展平一棵树(列表列表)?