Web Development

  Homes arrow Web Development arrow Professional Perl Part 2 - Passing Parameters
 Webmaster Tools
 
Base64 Encoding 
Browser Settings 
CSS Coder 
CSS Navigation Menu 
Datetime Converter 
DHTML Tooltip 
Dig Utility 
DNS Utility 
Dropdown Menu 
Fetch Content 
Fetch Header 
Floating Layer 
htaccess Generator 
HTML to PHP 
HTML Encoder 
HTML Entities 
IP Convert 
Meta Tags 
Password Encryption
 
Password Strength
 
Pattern Extractor 
Ping Utility 
Pop-Up Window 
Regex Extractor 
Regex Match 
Scrollbar Color 
Source Viewer 
Syntax Highlighting 
URL Encoding 
Web Safe Colors 
Forums Sitemap 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us 
Contact Us 
Site Map 
Privacy Policy 
Support 
 USERNAME
 
 PASSWORD
 
 
  >>> SIGN UP!  
  Lost Password? 
WEB DEVELOPMENT

Professional Perl Part 2 - Passing Parameters
By: Developer Shed
  • Search For More Articles!
  • Disclaimer
  • Author Terms
  • Rating: 4 stars4 stars4 stars4 stars4 stars / 7
    2004-04-19

    Table of Contents:

    Rate this Article: Poor Best 
      ADD THIS ARTICLE TO:
      Del.ici.ous Digg
      Blink Simpy
      Google Spurl
      Y! MyWeb Furl
    Email Me Similar Content When Posted
    Add Developer Shed Article Feed To Your Site
    Email Article To Friend
    Print Version Of Article
    PDF Version Of Article
     
     

    SEARCH DEV MECHANIC

    TOOLS YOU CAN USE

    advertisement

    Professional Perl Part 2 - Passing Parameters
    by Wrox Books

    However, ignoring prototypes for the moment, we may pass any number of parameters to a subroutine:
    mysubroutine ("parameter1", "parameter2", 3, 4, @listparameter);
    

    It is helpful to think of the parentheses as a conventional list definition being passed to mysubroutine as a single list parameter - remove mysubroutine from the above statement and what we are left with is a list. This is not far from the truth, if we recall that declaring a subroutine prior to using it allows us to use it as if it were a built-in list operator. Consequently, arrays and hashes passed as arguments to subroutines are flattened into one list internally, just as they are when combined into a larger list.

    The parameters that are passed into a subroutine appear inside the subroutine as a list contained in the special variable @_. This variable is made local to each subroutine, just as $_ is inside nested foreach loops. The definition of @_ is thus unique to each subroutine, despite the fact that @_ is a package variable.

    One simple and common way to extract parameters passed to subroutine is simply to assign @_ to a list of scalar variables, like so:

    sub volume {
    ($height, $width, $length) = @_;
    return $height * $width * $length;
    }
    

    This gives us named scalar variables we can write code for more legibly, and also takes care of any aliasing problems that might otherwise occur (as we will see in a moment). Alternatively, we can use shift to pull values off the array one by one:

    sub volume {
    $height = shift;
    $width = shift;
    $length = shift;
    return $height * $width * $length;
    }
    

    This differs from the previous example in that it actually modifies @_, removing passed parameters from the front of the list. After all the shifts have been processed @_ may be empty or it may contain further passed parameters. We can use that to our advantage to write subroutines that only use some parameters and pass the rest on. For example, here is a speculative object method that is a wrapper for the volume function:

    sub volume {
    my $self = shift;   #remove the object passed as the first parameter
    return Functions::volume(@_);   #pass remaining parameters on
    }
    

    If it's brevity we are after, we can avoid assigning the contents of @_ to anything, and simply use the values of @_ directly. This version of volume is not as clear as the first, but makes up for it by being only one line long. As a result the workings of the subroutine are still fairly obvious:

    sub volume {
    return $_[0] * $_[1] * $_[2];
    }
    

    The @_ array is a local array defined when the subroutine is first entered. However, while the array is local, the values of @_ are aliases for the original parameters that were passed in to the subroutine. This means that, if the parameter was a variable, modifying the values in the @_ array modifies the original variable as well. Used unwisely this can be an excellent way to create hard to understand and difficult to maintain code, but if the purpose of a subroutine is to manipulate a list of values in a consistent and generic way, it can be surprisingly useful. Here is an example of such a subroutine that emulates the chomp function:

    #strip the line separator '$/' from the end of each passed string:
    sub mychomp {
    foreach (@_) {
    s|$/$||;
    }
    }
    

    This also happens to be a good demonstration of aliasing. The subroutine actually aliases twice over; once to alias the variables $string and @lines in the @_ array inside the subroutine, and again in the foreach loop that aliases the loop variable $_ to the values in the @_ array one by one.

    We can call this subroutine in the same way as the real chomp:

    mychomp $string;
    mychomp @lines;
    

    Modifying the passed arguments implies that they are modifiable in the first place. Passing a literal value rather than a variable will produce a syntax error. For example:

    mychomp "you can't touch this \n";
    

    This produces:

    Modification of a read-only value attempted at ...
    

    When we come to discuss prototypes we will see how we can define subroutines that can be checked for correct usage at compile-time. This means we can create a subroutine like mychomp that will produce a syntax error if used on a literal variable at compile-time, just like the real chomp.

    Passing Lists and Hashes

    We mentioned earlier, when we started on the subject of passed arguments, that passing lists and hashes directly into a subroutine causes list flattening to occur, just as it does with ordinary list definitions. Consequently, if we want to pass an array or hash to a subroutine, and keep it intact and separate from the other arguments, we need to take additional steps. Consider the following snippet of code:

    $message = "Testing";
    @count = (1, 2, 3);
    testing ($message, @count);   # calls 'testing' -- see below
    

    The array @count is flattened with $message in the @_ array created as a result of this subroutine, so as far as the subroutine is concerned the following call is actually identical:

    testing ("Testing", 1, 2, 3);
    

    In many cases this is exactly what we need. To read the subroutine parameters we can just extract the first scalar variable as the message and put everything else into the count:

    sub testing {
    ($message, @count) = @_;
    ...
    }
    

    Or, using shift:

    sub testing {
    $message = shift;
    # now we can use @_ directly in place of @count
    ...
    }
    

    The same principle works for hashes, which as far as the subroutine is concerned is just more values. It is up to the subroutine to pick up the contents of @_ and convert them back into a hash: "for sorrow", 2 => "for joy", 3 => "for health",4 => "for wealth", 5 => "for sickness", 6 => "for death");

    However, this only works because the last parameter we extract inside the subroutine absorbs all the remaining passed parameters. If we were to write the subroutine to pass the list first and then the scalar afterwards, all the parameters are absorbed into the list and the scalar is left undefined:

    sub testing {
    (@count, $message) = @_;   # ERROR
    print "@_";
    } 
    testing(1, 2, 3, "Testing");
    # results in @count = (1, 2, 3, "Testing") and $message = undef
    

    If we can define all our subroutines like this we won't have anything to worry about, but if we want to pass more than one list we still have a problem.

    If we attempt to pass both lists as-is, then extract them inside the subroutine, we end up with both lists into the first and the second left undefined:

    sub testing {
    my (@messages, @count) = @_; # wrong!
    print "@_";
    }
    @msgs = ("Testing", "Testing");
    @count = (1, 2, 3);
    testing(@msgs, @count);
    # results in @messages = ("Testing", "Testing", "Testing", 1, 2, 3) and
    # @count = ();
    

    The correct way to pass lists and hashes, and keep them intact and separate, is to pass references. Since a reference is a scalar, it is not flattened like the original value and so our data remains intact in the form that we originally supplied it:

    testing (["Testing", "Testing"], [1, 2, 3]);   # with two lists
    testing (\@messages, \@count);   # with two array variables
    testing ($aref1, $aref2);   # with two list references
    

    Inside the subroutine we then extract the two list references into scalar variables and dereference them using either @{$aref} or $aref->[index] to access the list values:

    sub testing {
    ($messages, $count) = @_;
    # print the testing messages
    foreach (@ {$messages}) {
    print "$_ ... ";
    }
    print "\n";
    # print the count; 
    foreach (@ {$count}) {
    print "$_! \n";
    }
    }
    

    Another benefit of this technique is efficiency; it is better to pass two scalar variables (the references) than it is to pass the original lists. The lists may contain values that are large both in size and number. Since Perl must store a local copy of the @_ array for every new subroutine call in the calling stack, passing references instead of large lists can save Perl a lot of time and memory.

    Converting Scalar Subroutines into List Processors

    Consider this subroutine, which capitalizes the first letter of the string that it is passed:

    sub capitalize {
    $_[0] = ucfirst(lc $_[0]);
    print "$_[0]";
    }
    $country = "england";
    capitalize($country);   # produces 'England'
    

    Simple enough, but it only works on one string at a time. However, just because we wrote this subroutine to work as a scalar operator does not alter the fact that in reality it is working on a list. We have just limited it to handle a list with one value. With only a little extra effort we can turn this subroutine into something that works on scalars and lists alike:

    sub capitalize {
    foreach (@_) {
    $_ = ucfirst lc;   # lc uses $_ if argument is omitted
    print "$_[0]";
    }
    }
    

    Or more efficiently, with map:

    sub capitalize {
    map {$_ = ucfirst lc} @_;
    print "$_[0]";
    }
    

    This version works identically for calls like the above that pass only one parameter, but also happily works on arrays too:

    sub capitalize {
    map {$_ = ucfirst lc} @_;
    print "@_[0, 1, 2]";
    }
    @countries = ("england", "scotland", "wales");
    capitalize (@countries);   # produces ("England", "Scotland", "Wales")
    

    Passing '@_' Directly into Subroutines

    We said earlier that the @_ array is distinct to each subroutine and masks any previous definition. That is almost true - there is one exception provided, for reasons of efficiency, to the Perl programmers dedicated to optimizing their code. Normally @_ is defined locally, on entry to each subroutine. So, if we pass in no parameters at all we get an empty array. However, if we call a subroutine using the & prefix and do not pass parameters or use braces then the subroutine inherits the @_ array of the calling subroutine directly:

    &mysubroutine;   # inherit @_ from parent 
    

    The problem with this technique is that it is rather arcane, and not obvious to the reader of our code. Therefore, if we use it, a comment to the effect that this is what we are doing (such as the one above) is highly recommended.

    As far as the subroutine is concerned this is no different to passing the @_ array as a parameter:

    mysubroutine(@_);
    

    Although, this may seem equivalent, in the second case the @_ array is copied each time the call is made. If @_ contains a large number of values, or many calls are made (for instance in a recursive subroutine) then this is potentially expensive. The &mysubroutine; notation passes the @_ array directly, without making a copy, and so avoids the unnecessary work. Whether this is worth the trouble or not is of course another matter. If @_ only contains a few elements, it is probably better to live with the very minor inefficiency of copying the array and use the explicit version.

    Note that the aliasing of the values in the @_ array to the original variables (if the parameter was a variable) happens in either case, so it is not necessary to resort to this practice if all we want to do is modify the variables that were passed to us.

    Named Parameters

    Unlike other languages such as C or Java, Perl does not have any way to define formal parameter names for subroutines. The closest it gets is prototypes combined with retrieving parameters as lexical variables, as in:

    sub surname {
    my ($scalar1, $scalar2, @listarg) = @_;
    ...
    }
    

    However, we can implement named parameters using a hash. This provides an elegant way to pass in parameters without having to define them formally. The trick is to assign the @_ array to a hash variable. This converts the passed list into key-value pairs:

    sub volume {
    my %param = @_;
    return $param{'height'} * $param{'width'} * $param{'length'};
    }
    

    The disadvantage of this approach is that we have to name all the parameters that we pass. It is also slower, since hashes are inherently slower than arrays in use. The advantage is that we can add more parameters without forcing the caller to supply parameters that are not needed. Of course, it also falls upon us to actually check the arguments passed and complain if the caller sends us arguments that we do not use.

    We can call this subroutine using the => operator to make it clear that we are passing named parameters:

    volume (height => 1, width => 4, length => 9);
    

    We can also write the subroutine so that it accepts both named parameters and a simple list. One common technique borrowed from UNIX command line switches is to prefix named arguments with a minus, to distinguish them from unnamed arguments. To determine how the subroutine has been called, we just check the first character of the first parameter to see if it is a minus:

    sub volume {
    my %param;
    if ($_[0]=~/^-/) { # if the first argument starts '-', 
    assume named # arguments
    while (@_) {
    my ($key, $value)=(shift, shift);
    # check all names are legal ones
    die "Invalid name '$key'" 
    if $key!~/^-(height|width|length|color|density)$/;
    $key =~ s/^-//;   #remove leading minus
    $param{$key} = $value;
    }
    } else {
    # no '-' on first argument - assume list arguments
    $param{'height'} = shift;
    $param{'width'} = shift;
    $param{'length'} = shift;
    }
    foreach ('height', 'width', 'length') {
    unless (defined $param{$_}) {
    warn "Undefined $_, assuming 1";
    $param{$_} = 1;
    }
    }
    return $param{'height'} * $param{'width'} * $param{'length'};
    }
    

    In this version of the volume subroutine we handle both simple and named parameters. For named parameters we have also taken advantage of the fact that we know the names of the parameters to report a handy informative warning if any of them are undefined.

    Named parameters allow us to create a common set of parameters and then add or override parameters. This makes use of the fact that if we define a hash key twice, the second definition overrides the first:

    # define some default parameters
    %default = (-height => 1, -width => 4, -length => 9);
    # use default
    print volume(%default);
    # override default
    print volume(%default, -length => 16);
    print volume(%default, -width => 6, -length => 10);
    # specify additional parameters
    print volume(%default, -color => "red", -density => "13.4");     
    

    Before leaving the subject of named parameters, it is worth briefly mentioning the Alias module, available from CPAN. Alias provides the subroutines alias and attr, which generates aliases from a list of key-value pairs. Both subroutines use typeglobs to do the job.

    The alias subroutine takes a list of key-value pairs as its argument, and is therefore suited to subroutines. The type of variable defined by the alias is determined by the type of value it is aliased to; a string creates a scalar, a list creates an array. Here is yet another volume subroutine that uses alias:

    #!/usr/bin/perl
    # volalias.pl
    use warnings;
    use strict;
    no strict 'vars';
    use Alias;
    # subroutine using 'alias'
    sub volume {
    alias @_;
    return $height * $width * $length;
    }
    # a call to the subroutine
    print volume(height => 1, length => 9, color => 'red', width => 4);
    # aliased variables visible here
    print " = $height x $width x $length \n";
    

    However, alias suffers from three serious deficiencies. The first is that it is not compatible with strict vars; if we want strict variables we will have to declare all the aliased variables with use vars or (preferably) our. Another is that alias creates global aliases that persist outside the subroutine, which is not conducive to good programming. The third is that if we only use the variable once we'll get a warning from Perl about it. The script above does not do that because of the last line. Comment out that line, and all three variables will generate used only once warnings.

    attr takes a reference to a hash and creates aliases based on the keys and values in it. attr $hashref is similar to alias %{$hashref}, but localizes the aliases that it creates. It is ideal to use with object methods for objects based around hashes since each object attribute becomes a variable (hence the name):

    #!/usr/bin/perl 
    # attr.pl
    use warnings;
    use strict;
    {
    package Testing;
    use Alias;
    no strict 'vars';   # to avoid declaring vars
    sub new {
    return bless {
    count => [3, 2, 1],
    message => 'Liftoff!',
    }, shift;
    }
    sub change {
    # define @count and $message locally
    attr(shift);
    # this relies on 'shift' being a hash reference
    @count = (1, 2, 3);
    $message = 'Testing, Testing';
    }
    }
    my $object = new Testing;
    print "Before: ", $object?>{'message'}, "\n";
    $object?>change;
    print "After : ", $object?>{'message'}, "\n";
    print $Testing::message, "\n";   # warning - 'attr' vars do not persist
    close Testing::count;
    

    We can also define 'constants' with the const subroutine. This is actually just an alias for alias (it's even defined using alias inside the module, and must be imported explicitly:

    # const.pl
    use Alias qw(const);   # add 'alias' and/or 'attr' too, if needed
    const MESSAGE => 'Testing';
    print $MESSAGE, "\n";
    

    Attempting to modify the value of a constant produces an error:

    # ERROR: produce 'Modification of a read?only value attempted at ...'
    $MESSAGE = 'Liftoff!';
    

    The Alias module also provides several customization features, mainly for the attr subroutine, which allows us to control what gets aliased and how. Refer to 'perldoc Alias' for a rundown and some more examples.


    DISCLAIMER: The content provided in this article is not warranted or guaranteed by Developer Shed, Inc. The content provided is intended for entertainment and/or educational purposes in order to introduce to the reader key ideas, concepts, and/or product reviews. As such it is incumbent upon the reader to employ real-world tactics for security and implementation of best practices. We are not liable for any negative consequences that may result from implementing any information covered in our articles or tutorials. If this is a hardware review, it is not recommended to open and/or modify your hardware.

    More Web Development Articles
    More By Developer Shed

       

    WEB DEVELOPMENT ARTICLES

    - On Page SEO for New Domains
    - Improve Your Site`s Speed
    - Safari Books Online Review
    - Creating an Estore From the Ground Up
    - Most Common SEO Mistakes Developers Make
    - Making the Most of Your Titles and Meta Desc...
    - Five Ways Using Flash Can Damage Your Site
    - A Web Designer`s Guide to Colors
    - Use Webstarts to Create a Free Site
    - More Than Just Looks. How Your Web Design C...
    - How to Design Content Pages
    - Mint Review
    - Make Your WordPress Website Look Professional
    - How to Create a Mobile Web Site
    - Meta Tags: Still Useful?

    Developer Shed Affiliates

     



    © 2003-2018 by Developer Shed. All rights reserved. DS Cluster - Follow our Sitemap