开发者

better way for converting multidimensional array to one dimensional array

Currently, I am using following code to convert an irregular multidimensional array into one dimensional array.

my $array = [0, 
        [1],
        2,
        [3, 4, 5],
        [6, 
            [7, 8, 9 ],
        ],
        [10],
        11,
        ];

my @mylist;
getList($array);

print Dumper (\@mylist);


sub getList

{

        my $array = shift;

        return if (!defined $array);
        if (ref $array eq "ARRAY")
        {
               foreach my $i (@$array)
               {
                  开发者_Python百科 getList($i);
               }
        }
        else
        {
               print "pushing $array\n";
               push (@mylist, $array);
        }
}

This is based on recursion where I am checking each element. If element is a reference to an array then calling it recursively with new array.

Is there a better way to solve this kind of problem?


First of all your function should never return data by modifying a global variable. Return a list instead.

As for efficiency, Perl has a surprisingly large function call overhead. Therefore for large data structures I would prefer a non-recursive approach. Like so:

use Data::Dumper;
my $array = [
  0, 
  [1],
  2,
  [3, 4, 5],
  [6, [7, 8, 9 ]],
  [10],
  11,
];

my @mylist = get_list($array);

print Dumper (\@mylist);

sub get_list {
    my @work = @_;
    my @result;
    while (@work) {
        my $next = shift @work;
        if (ref($next) eq 'ARRAY') {
            unshift @work, @$next;
        }
        else {
            push @result, $next;
        }
    }
    return @result;
}

Note that the formatting that I am using here matches the recommendations of perlstyle. We all know the futility of arguing the One True Brace Style. But at the least I'm going to suggest that you reduce your 8 space indent. There is research into this, and code comprehension has been shown to be improved with indents in the 2-4 space range. Read Code Complete for details. It doesn't matter where you are in that range for young people, but older programmers whose eyesight is going find 4 a better indent. Read Perl Best Practices for more on that.


Use CPAN. Do not worry about recursion overhead until you know it is a problem.

#!/usr/bin/perl
use strict;
use warnings;
use List::Flatten::Recursive;

my $array = [
  0, 
  [1],
  2,
  [3, 4, 5],
  [6, [7, 8, 9 ]],
  [10],
  11,
];

my @result = flat($array);
print join(", ", @result), "\n";


It's generally better to replace recursion with iteration. For general techniques, see Higher Order Perl book (freely avaialble) chapter 5, in this case:

my @stack = ($array);
my @flattened;
while (@stack) {
    my $first = shift @stack;
    if (ref($first) eq ref([])) {
        push @stack, @$first; # Use unshift to keep the "order"
    } else {
        push @flattened, $first;
    }
}

The reason it's better is because recursive implementations:

  • Risk running into stack overflow if there are too many nested levels

  • Less efficient due to the cost of recursive calls


In generall this is the only way to do this.

You can optimize your code a little, by only caling getList() again, when you encounter a ArrayRef. If you find a regular value you can push it directly into @mylist instead of rerunning getList().


I've used this is in the past. This code is on the command line, but you can put the code in single quotes into your .pl file

$ perl -le'
use Data::Dumper;
my @array = ( 1, 2, 3, [ 4, 5, 6, [ 7, 8, 9 ] ], [ 10, 11, 12, [ 13, 14, 15 ] ], 16, 17, 18 );
sub flatten { map ref eq q[ARRAY] ? flatten( @$_ ) : $_, @_ }
my @flat = flatten @array;
print Dumper \@flat;
'
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜