Mallik wrote:

Dear Friends,

I need to pass 3 parameters to a subroutine, in which the
first parameter is an array and the last two parameters
are strings.

@abc = qw(1 2 3);
$x = 4;
$y = 5;

testsub(@abc, $x, $y);

Hello Mallik,


I've just read this thread and I'd like to add one more way to solve your problem in addition to solutions already posted by Joseph, William, Jim and David.

This is quite a common and simple problem for which reading the whole perldoc perlsub might be somewhat too confusing, but asking about it on mailing lists usually starts discussions which often become arguments on whose favorite style is better, so I'll do my best to describe every possible way as objectively as I can, and then I'll say which style I would personally use in which circumstances.

In general, you can pass an array to a subroutine in two ways: as a flattened list of scalars (its elements), or by reference. (Every subroutine call in Perl passes the arguments as a single flat list of scalars, but one of those scalars might just happen to be a reference to your array.)

Passing by reference is faster (because you copy only one scalar) and uses less memory but it doesn't matter much with short arrays. With passing by reference the subroutine can modify the original array itself (which may be useful or not, depanding on what you are doing).

Either way the subroutine can modify the array's _elements_ because the @_ array in subroutine holds aliases (not copies) of the arguments list.

Probably the most simple way to solve your problem without the need to even understand references (but it's still useful to read perldoc perlref) is to pop the last two elements of arguments array:

sub routine {

    my $y = pop;
    my $x = pop;

Remember to do it in reversed order, because pop always pops the last element. Now you have the last two arguments in $x and $y and the rest aliased in @_ array, just like you would with a normal call without $x and $y. Now, you can also add:

my @a = @_;

so the elements of @_ are copied to @a and you can modify them without changing the original values in the passed array. (But if those values are references to other data, you may still change the data they point to.)

The above method (1) works with passing the original array as a flat list of scalars. You call it like this:

routine(@array, $x, $y);

Now, as other people have already suggested, you can pass the array by reference either (2) explicitly:

routine([EMAIL PROTECTED], $x, $y);

or (3) implicitly, using the same syntax as with 1:

routine(@array, $x, $y);

For the last one (3) you need a prototype ([EMAIL PROTECTED]) and the body of your subroutine is exactly the same as with the previous one with explicit reference (2).

Now, which one of those ways you choose is probably a matter of taste, and depands on your specific performance needs. I won't tell you what you should choose yourself, but this is how I usually do it:

If I don't need to modify the array and it is just few elements long, I'd probably use the first way described here with flat arguments.

If the array is much longer and the performance hit of the first way of passing arguments is significant, I might use the the second or the third way. The third way has the benefit of being compatible with the first one from the caller perspective, so you can upgrade from 1 to 3 when your arrays start getting longer and still have the same interface without breaking any working code using your subroutine before the upgrade.

If I need to modify the array itself, I have no choice but to pass it by reference, and I would probably use the second way with explicit reference, to make it clear to the user that the reference to the original array is being passed and the array may be modified.

So, in other words, there is no one best way. I personally use every one of them depanding on what I do. Use whatever is best for a given task and always remember to document your interface so other people have no doubt how they should use it.

As for the prototypes, they are useful for two other things besides implicit references. You can use them to use your subroutines as unary or list operators without parentheses and much more importantly to verify if your subroutine was called correctly, but for that there are also other, maybe even better solutions, e.g.:

use Carp;
my $badarg = "Bad arguments for somesub\n";
sub somesub {
  my $y = pop or croak $badarg;
  my $x = pop or croak $badarg;
  # ... etc.
}

There are lots of useful ways to pass subroutine arguments in Perl. You can pass named arguments as a hash. Very useful if you have lots of optional arguments or don't want to remember their order:

use Carp;
my $badarg = "Bad arguments for namedsub\n";
sub namedsub {
  my %args = @_;
  # 'array' must be an array reference:
  croak $badarg unless $args{array} and ref $args{array} eq 'ARRAY';
  # 'x' must exist but can be undef:
  croak $badarg unless exists $args{x};
  # 'y' must be defined:
  croak $badarg unless defined $args{y};
  # ...
}

and call it:

namedsub(y => 10, z => 5, array => [EMAIL PROTECTED], x => 20);

or in any order you like. This is how many functions in CGI.pm module work, for example. As always, There's More Than One Way To Do It.

--
ZSDC Perl and Systems Security Consulting


-- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED] <http://learn.perl.org/> <http://learn.perl.org/first-response>




Reply via email to