【问题标题】:Perl: What is the easiest way to flatten a multidimensional array?Perl:展平多维数组的最简单方法是什么?
【发布时间】:2011-07-07 04:53:04
【问题描述】:

扁平化多维数组最简单的方法是什么?

【问题讨论】:

    标签: perl list multidimensional-array flatten


    【解决方案1】:

    使用地图的一级展平

    $ref = [[1,2,3,4],[5,6,7,8]]; # AoA
    
    @a = map {@$_} @$ref;         # flattens it
    
    print "@a";                   # 1 2 3 4 5 6 7 8
    

    【讨论】:

    • 只有当 $ref 被 arrayrefs 填充时才有效。如果它是标量和 arrayrefs 的混合,将不起作用。
    【解决方案2】:

    使用List::Flatten 似乎是最简单的:

    use List::Flatten;
    
    my @foo = (1, 2, [3, 4, 5], 6, [7, 8], 9);        
    my @bar = flat @foo;  # @bar contains 9 elements, same as (1 .. 9)
    

    实际上,该模块导出了一个简单的函数flat,所以你不妨复制source code

    sub flat(@) {
        return map { ref eq 'ARRAY' ? @$_ : $_ } @_;
    }
    

    您还可以将其 递归 以支持多个扁平化级别:

    sub flat {  # no prototype for this one to avoid warnings
        return map { ref eq 'ARRAY' ? flat(@$_) : $_ } @_;
    }
    

    【讨论】:

      【解决方案3】:

      最简单也是最自然的方法是遍历值并使用 @ 运算符“取消引用”/“解包”任何现有的嵌套值以获取组成部分。然后对遇到的每个参考值重复该过程。

      这类似于 Viajayenders 解决方案,但适用于尚未在数组引用中的值以及任何级别的嵌套:

      sub flatten {
        map { ref $_ ? flatten(@{$_}) : $_ } @_;
      }
      

      尝试像这样测试它:

      my @l1 = [ 1, [ 2, 3 ], [[[4]]], 5, [6], [[7]], [[8,9]] ];
      my @l2 = [ [1,2,3,4,5], [6,7,8,9] ];
      my @l3 = (1, 2, [3, 4, 5], 6, [7, 8], 9);  # Example from List::Flatten
      
      my @r1 = flatten(@l1);
      my @r2 = flatten(@l1);
      my @r3 = flatten(@l3);
      
      if (@r1 ~~ @r2 && @r2 ~~ @r3) { say "All list values equal"; }
      

      【讨论】:

      • 大同小异,但迭代而不是递归:@array=map { ref $_ eq 'ARRAY' ? @$_ : $_ } @array while grep ref $_ eq 'ARRAY', @array
      • 如果数组包含任何非数组引用,此答案将引发异常。您只检查值是否是引用,而不是引用是否是数组引用。提供哈希引用会导致错误“不是数组引用”。
      【解决方案4】:

      如果数据总是像一个例子,我也推荐 List::Flatten。

      但是数据有超过2个嵌套数组,平面不行。

      点赞@foo = [1, [2, [3, 4, 5]]]

      在这种情况下,您应该为它编写递归代码。

      下面怎么样。

      sub flatten {
        my $arg = @_ > 1 ? [@_] : shift;
        my @output = map {ref $_ eq 'ARRAY' ? flatten($_) : $_} @$arg;
        return @output;
      }
      
      my @foo = (1, 2, [3, 4, 5, [6, 7, 8]], 9);
      my $foo = [1, 2, [3, 4, 5, [6, 7, 8]], 9];
      my @output = flatten @foo;
      my @output2 = flatten $foo;
      print "@output";
      print "@output2";
      

      【讨论】:

      • 修改为首先缩短并接受数组引用。
      • 请注意,flatten 将在提供具有单个标量值的列表时返回空输出,或者在提供具有单个非数组引用的列表时抛出错误。例如,@input = (42) 将导致空输出(@{42} 导致空列表),而@input = ({ name => 'John Doe'}) 将导致错误(@{{name => 'John Doe'}} 导致“不是数组引用”)。
      【解决方案5】:

      当包含以下内容时,展平多维数组的最简单方法: 1. 数组 2.数组引用 3. 标量值 4. 标量引用

      sub flatten {
         map { ref $_ eq 'ARRAY' ? flatten(@{$_}) :
               ref $_ eq 'SCALAR' ? flatten(${$_}) : $_
         } @_;
      }
      

      另一个扁平子答案在标量引用上崩溃。

      【讨论】:

        【解决方案6】:

        类似的东西:

        my $i = 0;
        
        while ($i < scalar(@array)) {
            if (ref @array[$i] eq 'ARRAY') {
                splice @array, $i, 1, @$array[$i];
            } else {
                $i++;
            }
        }
        

        我瞎写的,不知道它是否真的有效,但你应该明白。

        【讨论】:

          【解决方案7】:

          与 Vijayender 的解决方案相同,但适用于包含数组引用和标量的混合数组。

          $ref = [[1,2,3,4],[5,6,7,8],9,10];
          @a = map { ref $_ eq "ARRAY" ? @$_ : $_ } @$ref;
          print "@a"
          

          当然你也可以扩展它来取消引用 hashrefs:

          @a = map { ref $_ eq "ARRAY" ? @$_ : ref $_ eq "HASH" ? %$_: $_ } $@ref;
          

          或使用 grep 清除垃圾:

          @a = map { @$_} grep { ref $_ eq 'ARRAY' } @$ref;
          

          从 List::MoreUtils 0.426 开始,我们有一个 arrayify 函数可以递归地展平数组:

          @a = (1, [[2], 3], 4, [5], 6, [7], 8, 9);
          @l = arrayify @a; # returns 1, 2, 3, 4, 5, 6, 7, 8, 9
          

          之前介绍过,但是坏了。

          【讨论】:

            猜你喜欢
            • 2010-10-27
            • 2012-02-06
            • 2021-05-20
            • 1970-01-01
            • 2020-05-22
            • 2021-11-11
            相关资源
            最近更新 更多