Softpanorama

May the source be with you, but remember the KISS principle ;-)
Contents Bulletin Scripting in shell and Perl Network troubleshooting History Humor

Introduction to Perl 5.10 for Unix System Administrators

(Perl 5.10 without excessive complexity)

by Dr Nikolai Bezroukov

Contents : Foreword : Ch01 : Ch02 : Ch03 : Ch04 : Ch05 : Ch06 : Ch07 : Ch08 :


Prev | Up | Contents | Down | Next

Subroutines, Functions Parameters and Return Values


In computer science we usually distinguish between subroutines and functions. Functions return some value and subroutines does not. Perl has only functions. That means that all subroutines return some value even if they do not have explicit return statement (see below).

Subroutines and functions may be placed anywhere in the script. Usually programmers prefer to put them iether all at the beginning or all at the end. If subroutines are few (for example just one) and short they look better at the beginning, otherwise end might be a better place.

A subroutine has a header sub after which you need to put its name and the body in {} brackets. For example:

sub hello_world {
   print "Hello world\n";
   print "Hello Perl\n";
}
You can call it using several different ways. Two main forms are to provide list of parameters (or just () in there is none) and the second is to use & in front of the name. For example:
hello_word(); 
&hello_world;

Parameters

Unlike C, Pascal and other high level languages in Perl the number of parameters to a subroutine is not fixed. In other words a subroutine can accept any number of parameters and this number can be different from one invocation to another. The only effect is the resulting size of the list array @_, a special array where Perl puts all the parameters passed to subroutine.

Perl also does not require to specify datatype of the parameters you are passing to subroutines. This is a typical approach used in scripting languages. It is also does not matter it the subroutine process the parameters that were passed. For example subroutine hello_world above has no parameters. Still we can call it as there are some, for example:

hello_world(); # Call the subroutine without parameters 
hello_world($_); # Call it with one parameter 
hello_world("Hello", "world"); # Call it with two parameters

Again, I would like to stress that it does not matter how many parameters were passed. When the subroutine is called, the list of parameters populates  the special array @_.  Naturally $_[0] represents the first parameter, $_[1] the second parameter and so on. Total number of parameters can be calculated as scalar(@_)

It is important to understand that this built-in array is different from the $_ scalar variable (so called default variable) which often contains the result of the last expression evaluated.

In  hte following example subroutine will  print first two parameters:

sub print_first_two parameters
{
   if (scalar(@_)>=2) { 
      print "Too few parameters were passed\n" 
    } else {
      print "The first parameter is: $_[0], the second parameter is: $_[1]\n"; 
    }

I would like to stress it again: the variables $_[0] and $_[1] have nothing to with the scalar $_ .

We can also print the list of all parameters that were passed to it:

sub print_all_param
{
   print "@_\n";
}

print_two_params("Hello", "world");	# two parameters
print_all_param("Hello", "Perl", "and", "goodby", "sanity"); # five parameters

Passing parameters by value vs. passing them by reference

Communication between the subroutine and calling program is performed in several ways. The first are  common variables (global variables as they are often called): in Perl the default scope of variables is the whole script.  The second are special communication variables called parameters. There are two classic methods of passing  parameters to a subroutine or function:

Passing Parameters by Reference

Despite the fact that parameters in Perl are assigned to the elements of the array @_, Perl passes parameters by reference. When you change the value of the element of the @_ array, you also change the value of the corresponding variable that was passed as the parameter to the subroutine. For example:

@X = (1,9,9,9);
print("Before call to the subroutine, the array contains: @X\n");
test_sub(@X);
print("After the call to the subroutine, the array contains: @X\n");

sub test_sub
{
    $_[0] = 2;
}

This program prints:

Before call to the subroutine, the array contains: 1 9 9 9
After the call to the subroutine, the array contains: 2 9 9 9
 
Besides subroutines parameters Perl uses pass by reference is few places where you might not expect

Passing elements of array by reference in foreach loop

As we discussed in chapter 1:

The foreach  statement provides a very convenient iteration mechanism without having to resort to a counter if one need to process all elements of an array. Therefore if task requires to scan an array checking each element, the foreach loop is a natural control structure to use. For example it can be used for finding the max/min (but built-in functions are better), various sums, selecting elements that satisfy some condition (if grep is not suitable for the task), etc.

The idea is very simple -- the body of the loop is executed once for every element of an array from the starting element to the end.  On each iteration the value of the current element is assigned to a selected temporary variable:

@month=( 'Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
foreach $m (@month)	{	# Visit each item in turn				
	print "$m\n";		# Print the item

}

Here the loop control variable is $m. But it is a regular Perl variable. It is actually a reference to the element of array we process and is you change it in the loop you will change the element of the array.

Again, I would like to stress that the controlling variable in foreach loop is actually an alias to the particular element which you are processing not a new variable. Or reference (we will learn about references in the next chapter).

This feature of Perl is often misunderstood. For example Damian Conway incorrectly documented this side effect in his book  Perl best practices:

Always declare a for loop iterator variable with my.

When using an explicit iterator variable in a for loop, make sure it's explicitly declared as a lexical variable, using the my keyword. That is, never write a for loop like this:

    my $client;

    SEARCH:
    for $client (@clients) {
        last SEARCH if $client->holding(  );
    }

    if ($client) {
        $client->resume_conversation(  );
    }

If you leave off the my, Perl doesn't reuse the lexical variable declared above the loop. Instead, it silently declares a new lexical variable (which is also named $client) as the iterator variable. That new lexical is always scoped to the loop block, and it hides any variable of the same name from any outer scope.

This behavior is contrary to all reasonable expectation. Everywhere else in Perl, when you declare a lexical variable, it's visible throughout the remainder of its scope, unless another explicit my declaration hides it. So it's natural to expect that the $client variable used in the for loop is the same lexical $client variable that was declared before the loop.

But it isn't. The previous example is actually equivalent to:

    my $client;

    SEARCH:
    for my $some_other_variable_also_named_client (@clients) {
        last SEARCH if $some_other_variable_also_named_client->holding(  );
    }

    if ($client) {
        $client->resume_conversation(  );
    }

Writing it that way makes the logical error in the code much more obvious. The loop isn't setting the outermost lexical $client to the first client who's on hold. It's setting an inner lexical variable (which is also named $client in the original version). Then it's throwing that variable away at the end of the loop. The outer lexical $client retains its original undefined value, and the if block is never executed.

Unfortunately, the first version shown doesn't make that error obvious at all. It looks like it ought to work. It would work if the loop construct was anything other than a for. And that's the problem. Finding this particular bug is made very much more difficult by the counter-intuitive semantics of loop iterators.

He forgot that Perl does not create any new variable in iterator variable of foreach loop. So his explanation is incorrect and Perl behavior is logical.

Passing Parameters by Value

Generally, passing by reference is dangerous because it does not isolate the subroutine from calling program. So only those parameters that are necessary should be passed to subroutine by reference. All other should be passed by value. There are two ways to achieve this in Perl:

You can provide an expression instead of the name of the variable. For numeric value adding zero is enough (enclosing the variable in parenthesis does not work). One can also use some function that does some useful conversion of the variable like uc and lc, etc. It would be logical to assume that using scalar built-in function also solves the problem but for some strange reason it does not work as expected.

$a= 'abba';
print("Before the call to the subroutine, the variable contains: $a\n");
test_sub(uc($a));
print("After the call to the subroutine, the variable contains: $a\n");

sub test_sub
{
    $_[0] = 0;
}

This program prints:

Before the call to the subroutine, the variable contains: abba
After the call to the subroutine, the variable contains: abba

It is often more convenient to imitate passing argument by value by assigning them to local variables. For example:

$a= 'abba';
print("Before call to the subroutine, the variable contains: $a\n");
test_sub($a));
print("After the call to the subroutine, the variable contains: $a\n");

sub test_sub{
    my temp=$_[0];
    temp = 0; # this will not change the value of $a
}

Using function shift to imitate passing parameter by value

Function shift is very convenient for populating a few parameters into local valuables, thus imitating call by value. For example:

$a='abba';
print("Before call to the subroutine, the variable contains: $a\n");
test_sub(uc($a));
print("After the call to the subroutine, the variable contains: $a\n");

sub test_sub{
   $arg1=shift;
}

Passing lists and arrays as parameters

Now that you understand about the scope of variables, let's take another look at parameters. Because all parameters in Perl are passed to a function in one array. It does not matter whether you pass a scalar and an array in the list of parameters or two arrays, they will be merged into array @_. There is no way to tell what you got as parameters scalar and array, two arrays or a set of scalars unless you use some  convention in passing your parameters.

Similarly, if two arrays are passed they are merged into one and there is no way to tell where the first ends and the second starts. This effect is called flattening of arrays.  For example:

@X=(1..3);
$plus=5;
print "before increment: @X\n";
incr_array(@X, $plus);
print "after increment: @X\n";

sub incr_array
{
my (@array, $increment) = @_;
   for ($i=0; $i < @array; $i++) {    
     $_[$i]=$_[$i]+$increment;
   }
}

This program prints:

before increment: 1 2 3 
after increment: 1 2 3 

That is not what we want. The reason is that when the local variables are initialized, the @array variables grab all of the elements in the @_ array, leaving none for the scalar variable. This results in the uninitialized variable $increment and wrong output (the value undef is converted to zero in numeric context). You can fix this by merely reversing the order of parameters: if the scalar value comes first, then the function processes the parameters without a problem.

@X=(1..10); 
print "before increment: @X\n"; 
incr_array(100, @X); 
print "after increment: @X\n";

sub incr_array 
{
my ($increment,@array) = @_;
   for ($i=1; $i <= @array; $i++) {
      $_[$i]=$_[$i]+$increment;
   }
}
You can pass as many scalar values as you want to a function, but only one array. If you try to pass more than one array, the array elements become joined together and passed as one array to the function. Your function won't be able to tell when one array starts and another ends.

In the next chapter we will learn how to solve this problem when we discuss references

Returning Data in Functions

You can return a scalar value using return statement:

sub max2
{
   if ( $_[0] > $_[1] ) {
      return $_[0];
   }
   return $_[1];
}
$big = max2($a, $b);

If case subroutines ends without executing return statement the value returned is that value of the last expression evaluated. This is not a recommended usage, so the example below is an example of a bad Perl style:

sub max2
{
   if ($_[0] > $_[1]){
      $_[0];
    } else{
      $_[1];
    }
$big = max2($a, $b);

Here is a variant of max for the list (adapted from perlsub):

    sub max {
        my t;
        my $max = shift(@_);
        foreach $t (@_) {
            $max = $ if $max < $t;
        }
        return $max;
    }
    $bestprice = max($amazon,$buy_com,$egghead,$tiger_direct);

 

 



Etc

FAIR USE NOTICE This site contains copyrighted material the use of which has not always been specifically authorized by the copyright owner. We are making such material available in our efforts to advance understanding of environmental, political, human rights, economic, democracy, scientific, and social justice issues, etc. We believe this constitutes a 'fair use' of any such copyrighted material as provided for in section 107 of the US Copyright Law. In accordance with Title 17 U.S.C. Section 107, the material on this site is distributed without profit exclusivly for research and educational purposes.   If you wish to use copyrighted material from this site for purposes of your own that go beyond 'fair use', you must obtain permission from the copyright owner. 

ABUSE: IPs or network segments from which we detect a stream of probes might be blocked for no less then 90 days. Multiple types of probes increase this period.  

Society

Groupthink : Two Party System as Polyarchy : Corruption of Regulators : Bureaucracies : Understanding Micromanagers and Control Freaks : Toxic Managers :   Harvard Mafia : Diplomatic Communication : Surviving a Bad Performance Review : Insufficient Retirement Funds as Immanent Problem of Neoliberal Regime : PseudoScience : Who Rules America : Neoliberalism  : The Iron Law of Oligarchy : Libertarian Philosophy

Quotes

War and Peace : Skeptical Finance : John Kenneth Galbraith :Talleyrand : Oscar Wilde : Otto Von Bismarck : Keynes : George Carlin : Skeptics : Propaganda  : SE quotes : Language Design and Programming Quotes : Random IT-related quotesSomerset Maugham : Marcus Aurelius : Kurt Vonnegut : Eric Hoffer : Winston Churchill : Napoleon Bonaparte : Ambrose BierceBernard Shaw : Mark Twain Quotes

Bulletin:

Vol 25, No.12 (December, 2013) Rational Fools vs. Efficient Crooks The efficient markets hypothesis : Political Skeptic Bulletin, 2013 : Unemployment Bulletin, 2010 :  Vol 23, No.10 (October, 2011) An observation about corporate security departments : Slightly Skeptical Euromaydan Chronicles, June 2014 : Greenspan legacy bulletin, 2008 : Vol 25, No.10 (October, 2013) Cryptolocker Trojan (Win32/Crilock.A) : Vol 25, No.08 (August, 2013) Cloud providers as intelligence collection hubs : Financial Humor Bulletin, 2010 : Inequality Bulletin, 2009 : Financial Humor Bulletin, 2008 : Copyleft Problems Bulletin, 2004 : Financial Humor Bulletin, 2011 : Energy Bulletin, 2010 : Malware Protection Bulletin, 2010 : Vol 26, No.1 (January, 2013) Object-Oriented Cult : Political Skeptic Bulletin, 2011 : Vol 23, No.11 (November, 2011) Softpanorama classification of sysadmin horror stories : Vol 25, No.05 (May, 2013) Corporate bullshit as a communication method  : Vol 25, No.06 (June, 2013) A Note on the Relationship of Brooks Law and Conway Law

History:

Fifty glorious years (1950-2000): the triumph of the US computer engineering : Donald Knuth : TAoCP and its Influence of Computer Science : Richard Stallman : Linus Torvalds  : Larry Wall  : John K. Ousterhout : CTSS : Multix OS Unix History : Unix shell history : VI editor : History of pipes concept : Solaris : MS DOSProgramming Languages History : PL/1 : Simula 67 : C : History of GCC developmentScripting Languages : Perl history   : OS History : Mail : DNS : SSH : CPU Instruction Sets : SPARC systems 1987-2006 : Norton Commander : Norton Utilities : Norton Ghost : Frontpage history : Malware Defense History : GNU Screen : OSS early history

Classic books:

The Peter Principle : Parkinson Law : 1984 : The Mythical Man-MonthHow to Solve It by George Polya : The Art of Computer Programming : The Elements of Programming Style : The Unix Haterís Handbook : The Jargon file : The True Believer : Programming Pearls : The Good Soldier Svejk : The Power Elite

Most popular humor pages:

Manifest of the Softpanorama IT Slacker Society : Ten Commandments of the IT Slackers Society : Computer Humor Collection : BSD Logo Story : The Cuckoo's Egg : IT Slang : C++ Humor : ARE YOU A BBS ADDICT? : The Perl Purity Test : Object oriented programmers of all nations : Financial Humor : Financial Humor Bulletin, 2008 : Financial Humor Bulletin, 2010 : The Most Comprehensive Collection of Editor-related Humor : Programming Language Humor : Goldman Sachs related humor : Greenspan humor : C Humor : Scripting Humor : Real Programmers Humor : Web Humor : GPL-related Humor : OFM Humor : Politically Incorrect Humor : IDS Humor : "Linux Sucks" Humor : Russian Musical Humor : Best Russian Programmer Humor : Microsoft plans to buy Catholic Church : Richard Stallman Related Humor : Admin Humor : Perl-related Humor : Linus Torvalds Related humor : PseudoScience Related Humor : Networking Humor : Shell Humor : Financial Humor Bulletin, 2011 : Financial Humor Bulletin, 2012 : Financial Humor Bulletin, 2013 : Java Humor : Software Engineering Humor : Sun Solaris Related Humor : Education Humor : IBM Humor : Assembler-related Humor : VIM Humor : Computer Viruses Humor : Bright tomorrow is rescheduled to a day after tomorrow : Classic Computer Humor

The Last but not Least


Copyright © 1996-2016 by Dr. Nikolai Bezroukov. www.softpanorama.org was created as a service to the UN Sustainable Development Networking Programme (SDNP) in the author free time. This document is an industrial compilation designed and created exclusively for educational use and is distributed under the Softpanorama Content License.

The site uses AdSense so you need to be aware of Google privacy policy. You you do not want to be tracked by Google please disable Javascript for this site. This site is perfectly usable without Javascript.

Original materials copyright belong to respective owners. Quotes are made for educational purposes only in compliance with the fair use doctrine.

FAIR USE NOTICE This site contains copyrighted material the use of which has not always been specifically authorized by the copyright owner. We are making such material available to advance understanding of computer science, IT technology, economic, scientific, and social issues. We believe this constitutes a 'fair use' of any such copyrighted material as provided by section 107 of the US Copyright Law according to which such material can be distributed without profit exclusively for research and educational purposes.

This is a Spartan WHYFF (We Help You For Free) site written by people for whom English is not a native language. Grammar and spelling errors should be expected. The site contain some broken links as it develops like a living tree...

You can use PayPal to make a contribution, supporting development of this site and speed up access. In case softpanorama.org is down you can use the at softpanorama.info

Disclaimer:

The statements, views and opinions presented on this web page are those of the author (or referenced source) and are not endorsed by, nor do they necessarily reflect, the opinions of the author present and former employers, SDNP or any other organization the author may be associated with. We do not warrant the correctness of the information provided or its fitness for any purpose.

Last modified: October, 11, 2015