Sachin S
Sachin S

Reputation: 396

How can I flatten the arguments to my subroutine into an array?

Consider following script:

use strict;
use Data::Dumper;

my @arr=('1A','2A');
my $arr_ref=['1','2'];

sub routine1
{
my @arr=@_;
print Dumper(\@arr);
}

routine1(@arr,'one_A');

sub routine2
{
my $arr_ref=[@_];
print Dumper($arr_ref);
}

routine2($arr_ref,'one');

routine1 is using @arr and routine2 is using $arr_ref.

routine1 prints the following:

$VAR1 = [
         '1A',
         '2A',
         'one_A'
        ];

routine2 prints following:

$VAR1 = [
         [
           '1',
           '2'
         ],
         'one'
        ];

I want to continue using @_ and arr_ref in routine2 but want to come up with below output:

$VAR1 = [
         '1',
         '2'
         'one'
        ];

Can someone suggest the way out?

Upvotes: 1

Views: 177

Answers (3)

Andy Lester
Andy Lester

Reputation: 93765

Just wrote this the other day at work.

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

Upvotes: 1

Joel Berger
Joel Berger

Reputation: 20280

Using the function ref you can see if a scalar is a reference (and if so, which type). In a simplistic case where only array references will be passed you can simply use this to flatten the inputs.

#!/usr/bin/env perl

use strict;
use warnings;

use Data::Dumper;

sub test {
  my @arr = map { ref() ? @$_ : $_ } @_;
  print Dumper \@arr;
}

test( ['a', 'b'], 1 );

As a side benefit, this code will die with a message if a reference to another type is passed, since you attempt to deference as an array. If you need to handle more, you will need to check the reference type. This starts to build in complexity quickly.

#!/usr/bin/env perl

use strict;
use warnings;

use Data::Dumper;

sub test {
  my @arr = map { 
    my $type = ref;
    if ( ! $type ) {
      $_;
    } elsif ( $type eq 'ARRAY' ) {
      @$_;
    } elsif ( $type eq 'HASH' ) {
      %$_;
    } else {
      ()
    }
  } @_;
  print Dumper \@arr;
}

test( ['a', 'b'], { p => 'q' }, 1 );

By returning an empty list for other reference types I silently ignore all other reference types. Or perhaps you would rather force stringification on other reference types.

...
} else {
  "$_";
}
...
test( ['a','b'], sub{}, bless({},'MyClass'), 1 ); 

Of couse which of these handlings to use depends on you use case.

Upvotes: 3

Borodin
Borodin

Reputation: 126742

This program shows a subroutine flatten that will flatten a mixed list of simple data and array references, nested to any level.

use strict;
use warnings;

use Data::Dump;

my @arr = qw/ 1A 2A /;
my $arr_ref = [1, 2];

sub flatten;

routine1(@arr, 'one_A');
routine2($arr_ref, 'one');

sub routine1 {
  my @arr=@_;
  dd \@arr;
}

sub routine2 {
  my $arr_ref = [flatten @_];
  dd $arr_ref;
}

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

output

["1A", "2A", "one_A"]
[1, 2, "one"]

Upvotes: 0

Related Questions