MadraghRua has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks,

OK, I understand how to pass a single value into a subroutine - I even understand how to pass two strings into a subroutine.
So how do I pass an array and a string? Or even a hash and an array into subroutines?

For instance, I have the following code:

sub printEachExpt {  my @expNames= @_;  my ($total, $n, $outputPath);  $total = @expNames; $outputPath = $expNames[$total - 1];  print "The output path is $outputPath.\n";  print "The experiment files are:\n";  for ($n = 0; $n <= $total - 2; $n++) {   print "$outputPath/$expNames[$n]\n";  } return(@expNames, $outputPath);   } printEachExpt(@expNames, $outputPath);

What I'm putting in with the array are a number of names,
Con5.5A, Con6.5B, Con6.5C, Con6.5D. What I'm submitting with the $outputPath string is /export/home/madraghrua/argentina .
What I get is the following:

The output path is /export/home/madraghrua/argentina.
The experiment files are:
/export/home/madraghrua/argentina/Con6.5A
/export/home/madraghrua/argentina/Con6.5B
/export/home/madraghrua/argentina/Con6.5C
/export/home/madraghrua/argentina/Con6.5D

This is correct, but the input to the subroutine is concatenated together and I need to know which array element corresponds to what I need.
However, I would like to pass the array as an array and the string as a string. Or even two arrays. I pretty sure that I know I can do this in C - how do I do this in Perl? Is there a general way of passing variables for hashes as well?

Thank you in advance for your learned answers...
MadraghRua
yet another biologist hacking perl....

Replies are listed 'Best First'.
RE: Passing different values into subroutines
by Boogman (Scribe) on Aug 31, 2000 at 21:29 UTC
    To pass non-scalars into subroutines you can use references. For example, to pass an array you can do the following.
    my @array1 = ( 1, 2, 3, 4 ); my @array2 = ( 5, 6, 7, 9 ); printArrays( \@array1, \@array2 ); sub printArrays { my ( $first, $second ) = @_; print "@$first @$second\n"; }
    You can use references to pass hashes as well, you can even pass subroutines if you feel like it.

    Update: To apply this to your situation...
    sub printEachExpt { my ( $expNames, $outputPath ) = @_; my $total = @$expNames; print "The output path is $outputPath.\n"; print "The experiment files are:\n"; for ( 0 .. $#$expNames ) { print "$outputPath/$expNames->[$n]\n"; } return( $expNames, $outputPath); }
    That should be what you want to do... untested...

    Update: oh and you don't need that line with $total in there...
      sub printEachExpt { my @expNames = @{$_[0]}; # De-reference the array. my $outputPath = $_[1]; print "The output path is $outputPath.\n"; print "The experiment files are:\n"; print "$outputPath/$_\n" for @expNames; } # And to call it: printEachExpt( \@myExp, $mypath ); # -------------^ the \ means a reference to the @myExp
(kudra) Re: Passing different values into subroutines
by kudra (Vicar) on Aug 31, 2000 at 21:33 UTC
    If you want to pass 2+ arrays, 2+ hashes, or (1+ array and 1+ hash), you will need to pass by ref, as Boogman demonstrates.

    If you have just one array or hash, you can pass it as an array or a hash. You will just have to take your args out in the proper order (one way or another).

    See this node for details. It talks about shift, positional params, and pop (and refs, but you already have that info).

Re: Passing different values into subroutines
by Maclir (Curate) on Sep 01, 2000 at 03:06 UTC
    I have got in to trouble before trying to pass multiple parms to a subroutine that are not scalars. I believe that perl internally concatenates all parameters into a single array.

    As others have suggested, use references. To call a subroutine, I use:

    thisIsMySub( \@onearray, \%ahasd, \@secondarray, $scalar1, $scalar2);
    And in the subroutine itself, I use:
    my @firstarray = @{shift }; my %somehash = %{shift }; my @secondadrray = @{shift }; my $scalarone = shift; my $scalartwo = shift;
    I would gladly accept advice from others if there is a better way to do this.
      I believe that perl internally concatenates all parameters into a single array.

      Very close. Parameters are passed as a list. When you assign something to a list, there's no way for Perl to know how long the list is supposed to be, so it slurps up everything available. It's kinda like the greedy * operator that's come under fire recently (Death to Dot Star).

      If you need to get more than one list (whether list, array, or hash) to or from a subroutine, you need to use a reference, otherwise they'll blend together like a creamy fruit smoothie.

      Codewise, sometimes I explicitly dereference-and-copy my arguments like you do, and sometimes I just act on them directly:

      my $arr_ref = shift; foreach my $item (@$arr_ref) { $item .= "foo"; }