tags:

views:

75

answers:

3

I seem to recall (though can't find any reference now) to one being able to do something akin to

my @a = ("foo","bar");
my ($item1, $item2) = @a;

The above does not do what I want it to (obviously) but I seem to recall that there is some way to do this, where it loads the items associated with the order of the scalars in the parenthesized list.

For that matter I thought that's how the args array is passed into subroutines, as in...

sub method{
  my ($arg1, $arg2) = @_;
}

Maybe I'm just going out of my mind, but I thought this was possible.

[EDIT]

Ah...so based on the first answer I realize that the reason it's not working is that I'm using a two dimensional array. So, in my code it actually looks like this:

foreach(@twoDimenArray){
    my ($item1, $item2, $item3) = $_; #$_ is an array
}

It must be the $ syntax that's screwing it up but I've tried ($_) and @($_) and @$_ and none of those work.

+4  A: 

What you have works for me - that is, this prints "foo bar":

use strict;
use warnings;

my @a = ("foo","bar");
my ($item1, $item2) = @a;

print "$item1 $item2\n";

I happen to be using Perl 5.13.4 on MacOS X 10.6.4, but I don't think that's a significant factor; I'd expect any Perl 5.x to accept it.


This code implements a 2D array:

use strict;
use warnings;

my @a;

$a[0][0] = "a00";
$a[0][1] = "a01";
$a[1][0] = "a10";
$a[1][1] = "a11";

my ($item1, $item2) = @a;

print "$item1 $item2\n";

The output is 'two ARRAY refs':

ARRAY(0x100803068) ARRAY(0x100826770)

Off-hand, I don't know of a way to expand the array of arrays into 4 separate values in a single operation. That isn't quite the same as "there is no way to do it".


And, implementing your foreach loop:

foreach my $array (@a)
{
    my($item1, $item2) = @$array;
    print "$item1 $item2\n";
}

This prints:

a00 a01
a10 a11

This code is an example from the DBD::Informix distribution - last modified in 2002. It uses the fetchall_arrayref() method which returns an array of array references, as mentioned in one of the comments to the question.

#!/usr/bin/perl -w
#
# DBD::Informix Example 5 - fetchall_arrayref
#
# @(#)$Id: x05fetchall_arrayref.pl,v 100.1 2002/02/08 22:50:10 jleffler Exp $
#
# Copyright 1998 Jonathan Leffler
# Copyright 2000 Informix Software Inc
# Copyright 2002 IBM

use DBI;
printf("DEMO1 Sample DBD::Informix Program running.\n");
printf("Variant 4: using fetchall_arrayref()\n");
my($dbh) = DBI->connect("DBI:Informix:stores7") or die;
my($sth) = $dbh->prepare(q%
        SELECT fname, lname FROM customer WHERE lname < 'C'%) or die;
$sth->execute() or die;
my($ref) = $sth->fetchall_arrayref();
foreach $row (@$ref)
{
  printf("%s %s\n", $$row[0], $$row[1]);
}
undef $sth;
$dbh->disconnect();
printf("\nDEMO1 Sample Program over.\n\n");

Were I writing it now (or updating it), there'd be use use strict; as well as the -w (equivalent to use warnings;). It shows that back in 2002 (and ever since, AFAIK), you could write @$ref happily enough. These days, I'd probably write the $$row[0] references differently - as ${$row}[0].

Jonathan Leffler
That's weird a minute ago, someone (Cameron, I think) posted the right answer in a comment, and then deleted it. Anyway... leave it to perl to make things ever unpredictable. The answer is ($a,$b,$c) = @{$_}
Dr.Dredel
Comments once deleted are lost forever, AFAIK. With luck, the third edition of this answer also gives you the 'right answer'. And, also AFAIK, @$x and @{$x} are equivalent.
Jonathan Leffler
Heh... I deleted that comment because I tried it out at the command prompt but couldn't get it to work. My one-liner test was probably flawed somehow. I could add back the comment if you like ;-)
Cameron
Or you can just post a quick answer and I'll give you the green check mark... not sure why someone downvoted my question! was it really that dumb?! I couldn't find the syntax for this exact manner of assignment anywhere.
Dr.Dredel
@Jonatha, @$x did not work for me. But it was definitely one of my "poking around in the dark" candidates! :)
Dr.Dredel
@Dr.Dredel: that's curious - it definitely did for me, both in the implicit form (`foreach (@a) { my($i,$j) = @$_; }`) and the explicit form (`foreach my $x (@a) { my($i,$j) = @$x; }`. Which version of Perl are you using? (I tried it with a PPC 32-bit build of Perl 5.6.2 on my Intel Mac, and apart from problems finding strict and warnings, it worked fine with simply '`@$_`'.)
Jonathan Leffler
Jonathan, I'm on v5.10.0 on a mac 10.6. Is it possible that the .5 difference causes this for me? I kind of doubt it... I think I must be doing something else differently, though can't think of what offhand, and don't have the time now to experiment. Thanks very much for your help though, I did up vote your answer.
Dr.Dredel
I upvoted too, and also have no idea why `@{$_}` would work when `@$_` doesn't. They both work for me on ActiveState Perl 5.10.0 (Windows 7).
Cameron
@Dr.Dredel: odd - my builds of 5.10.0 and 5.10.1 both give me the answer quoted above, as does `/usr/bin/perl` which is also 5.10.0. I'm mildly curious about the context in DBI where you get the array of arrays - I suppose that would be `$sth->fetchall_arrayref`?
Jonathan Leffler
yep... but I have a method that gets whatever that fetch delivers and stores it in a local array (it's limited to n rows). I like knowing how large my result set is and because DBI is a stream it has no idea until it's done. But the point is that by the time I'm doing what you're helping me with, it's just an array of string arrays.
Dr.Dredel
oh, sorry... I just realized... no, I don't use fetchall, I use fetchrow_array. Seems like your way is better.
Dr.Dredel
@Dr.Dredel: fetchall_arrayref is a powerful weapon when used carefully; it is a dangerous memory eater when it is not used carefully. The DBD::Informix source includes examples - including x02fetchrow_array.pl and x03fetchrow_arrayref.pl. Maybe you should pull the source from CPAN to take a look? They're obviously written for DBI plus DBD::Informix, but generally shouldn't take much fixing to work with any other DBMS. Actually, your biggest problem would likely be getting hold of the schema and data for the demo database.
Jonathan Leffler
+6  A: 

Try using @{$_}:

foreach (@twoDimenArray) {
    my ($item1, $item2, $item3) = @{$_};    # $_ is an array
}
Cameron
A: 

For an array of array refs:

#!/usr/bin/perl

use strict;
use warnings;

my @ary = ( [ qw( foo bar ) ], [ qw( one two ) ], );

my ($ary00, $ary01, $ary10, $ary11) = map { @{ $_ } } @ary;

For arbitrarily-nested arrays:

#!/usr/bin/perl

use strict;
use warnings;

my @ary = (
    [
        qw( foo bar ),
        [
            qw( BAZ QUUX )
        ],
    ],
    [
        qw( ten eleven ),
        [
            'one hundred and twenty',
            'one hundred and twenty-one',
            [
                'one thousand two hundred and twenty',
                'one thousand two hundred and twenty-one',
            ],
        ],
    ],
);

sub flatten {
    my @ary = @_;
    return map { ref($_) eq 'ARRAY' ? flatten( @{ $_ } ) : @{ $_ } } @ary;
}

my ( $a00, $a01, $a020, $a021, $a10, $a11, $a120, $a121, $a1220, $a1221 ) = flatten @a;
Daniel Holz