1

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?

1
  • You're question is too vague for me. Commented Mar 30, 2013 at 18:40

3 Answers 3

3

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.

Sign up to request clarification or add additional context in comments.

4 Comments

Inside the subroutine, can we not take the values of @_ directly in array_reference (instead of in the @arr)? Is that possible?
In order to flatten the references, you are going to have to iterate through the structure. I suppose you could try to pack it back into @_, but why would you want to?
Why not just map @$_, @_?
@Zaid, because that doesn't work. Did you try it? Array deref on a scalar.
1

Just wrote this the other day at work.

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

3 Comments

I'm struggling to see what this would buy over a simple map @$_, @_
@zaid Well, it flattens a heterogeneous list and also flattens more-deeply-nested lists. In other words, it works on (5, [6], [7, 8], [ [ 9, 4], 5]).
@darch : Joel made me realize that
0

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"]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.