LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   perl: passing 2 individual arrays/lists as args (https://www.linuxquestions.org/questions/programming-9/perl-passing-2-individual-arrays-lists-as-args-314508/)

johnMG 04-18-2005 09:58 AM

perl: passing 2 individual arrays/lists as args
 
Suppose I'm writing a function that I want to operate on two different arrays:

Code:

#!/usr/bin/perl

use warnings;
use strict;

my @a = ( 1, 2, 3 );
my @b = qw/ foo bar /;

sub do_something_with_two_arrays
{
      my (@a1, @a2) = @_; # ? XXX
      print "In the first array arg passed, we have:\n";
      for ( @a1 ) { print "$_\n"; }
      print "And in the second array arg passed:\n";
      for ( @a2 ) { print "$_\n"; }
      # Maybe some processing follows...
}

print "-" x 40, "\n";
do_something_with_two_arrays @a, @b;
print "~" x 40, "\n";
do_something_with_two_arrays( @a, @b ); # Nope. Same as above.
print "=" x 40, "\n";

How does Perl know where the first array ends and the next one begins?

I'm looking at the docs for the built-in 'push' function. It says that operator takes an ARRAY and then also a LIST. What's the difference? How does push know where the first one ends and the next one begins?

johnMG 04-18-2005 10:05 AM

BTW, I realize that I can just use references to arrays to do what I want to do, however, I'd like to understand how builtins like 'push' do their magic.

aluser 04-18-2005 11:51 AM

IIRC, you can do
Code:

sub foo (@@) {
    my ($ref1, $ref2) = @_;
    ....
}

foo(@array1, @array2);

The only problem with this is that in some situations it's possible to compile the code which calls foo before compiling foo itself, in which case the calling code won't know about the (@@) business and so will just pass a flat array. Of course, the perl builtins are guaranteed to be compiled before any code that uses them.

sirclif 04-18-2005 12:53 PM

perl doesn't know where one ends and the other begins. I believe the solution is to use references. the fact that perl throws all of it's subroutine arguments into one big list is handy most of the time, unless your dealing with arrays. So what you would do is create references to the arrays (kind of like pointers) and pass the references to the sub, then dereference them in the sub. for example:

Code:

#!/usr/bin/perl

use warnings;
use strict;

my @a = ( 1, 2, 3 );
my @b = qw/ foo bar /;

$a_ref = \@a;
$b_ref = \@b;


do_something_with_two_arrays($a_ref, $b_ref);

do_something_with_two_arrays($b_ref, $a_ref);


sub do_something_with_two_arrays
{
      my ($a1_ref, $a2_ref) = @_;
      print "In the first array arg passed, we have:\n";
      for ( @$a1_ref ) { print "$_\n"; }
      print "And in the second array arg passed:\n";
      for ( @$a2_ref ) { print "$_\n"; }
      # Maybe some processing follows...
}

the \ operator creates a reference, in this case to an array (@). a reference is a scalar data. to dereference a reference, just put the appropieate symbol in front of the reference name. e.i.

scalar: $$ref
array: @$ref
hash: %$ref

hope this helps

johnMG 04-18-2005 12:57 PM

Ahh... that (@@) function declaration notation seems to be what I was missing. I'll look further into it. Thanks.

johnMG 04-18-2005 01:20 PM

Thanks sirclif. I've also found "man perlreftut" and, of course, "man perlref" pretty helpful as well. :)

sirclif 04-18-2005 01:27 PM

sorry i didn't see your replay saying that you already knew about references. the built in function push takes an array and 'tacks on' a scalar value or a list to the end of the array. there isn't much difference between an array and a list. an array is just a list with a name really. anyhow, given an array array1 you can push and pop elements on and off.

@array1 = (1,2,3);
push @array1, 4;

array1 now looks like (1,2,3,4)

vise versa if you pop the array into a scalar

$tmp = pop @array1;

tmp is now 4. but again, push, pop, unshift, and shift have no idea that an array is supposed to be more than one array, they just add and remove elements from the back and front of an array.

johnMG 04-18-2005 02:05 PM

> but again, push, pop, unshift, and shift have no idea that an
> array is supposed to be more than one array, they just add
> and remove elements from the back and front of an array.

Yes, but there's magic going on there. "push", of course, *modifies* @array1. If the args passed to "push" were all simply flattened into one list, there would be no way for "push" to know about the array it was supposed to modify. This is why it seems (to me) like "(@@)" is key. Looks like "man perlsub" explains it under the heading "Prototypes" (which I'm currently having a look at :).

Thanks.

sirclif 04-18-2005 02:16 PM

ah, i see what your saying. the builtin function push takes two arrays and still knows how to deal with it. buitins also do not require parentheses around thier arguments, or the & operator.

aluser 04-18-2005 02:39 PM

Quote:

buitins also do not require parentheses around thier arguments, or the & operator.
Actually, user functions don't either : )
Code:

15:38 aluser@alf:~$ perl -e 'sub foo { print "hi there $_[0]\n" } foo "bob"'
hi there bob


puffinman 04-18-2005 03:12 PM

Quote:

Actually, user functions don't either : )
Not having to use the & operator is only true if the subroutine is defined before it is used.

sirclif 04-18-2005 03:13 PM

wow, sure enought.
why can't i get it to work in a script file?

aluser 04-18-2005 03:49 PM

Quote:

why can't i get it to work in a script file?
Evidently you do need the parenthesis if you have the declaration of the sub after its use. This works:
Code:

#!/usr/bin/perl
use strict;
use warnings;

foo("bob");

sub foo {
        print "hi there $_[0]\n";
}

I'm not aware of any case where the & is required in a call to the function; I use it to take references and that's about it. Of course, it's a matter of style.

For user functions, I tend to always use parentheses; probably because I've had that error from not predeclaring the sub at some point and mostly because it makes things less clear not to have them.


All times are GMT -5. The time now is 03:35 AM.