【问题标题】: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 答案:

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

【问题讨论】:

    标签: list raku flatten


    【解决方案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 实现)
    猜你喜欢
    • 2021-09-17
    • 2022-01-17
    • 1970-01-01
    • 1970-01-01
    • 2012-03-28
    • 2018-01-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多