See the Troubleshooters.Com Bookstore.
The following outline shows referencing and de-referencing of variables.
Note that in the case of lists and hashes, you reference and dereference the
list or hash as a whole, not individual elements (at least not for the purposes
of this discussion).
These principles are demonstrated in the this source code. Note the following anomolies:
In computer science, there are two methods of passing arguments to a subroutine:
In general, I believe it's best to use arguments as input-only.
When passing by reference, the language makes the argument's exact variable available inside the subroutine, so any changes the subroutine makes to the argument affect the arguments value in the calling procedure (after the subroutine call, of course). This tends to reduce encapsulation, as there's no way of telling in the calling routine that the called routine changed it. Passing by reference harkens back to the days of global values, and in general creates less robust code.
All arguments in Perl are passed by reference! If the programmer wishes to make a copy of the argument to simulate passing by value (and I believe in most cases he should), he must explicitly make the copy in the subroutine and not otherwise access the original arguments.
Old school, no prototyping | |
Calling the subroutine | |
&mysub(); |
Old school, no prototyping | |
Constructing the subroutine | |
sub mysub |
Prototyping, no arguments | |
Calling the subroutine | |
mysub(); |
Prototyping, no arguments | |
Constructing the subroutine | |
sub mysub() |
Prototyping, two string arguments | |
Calling the subroutine | |
mysub($filename, $title); |
Prototyping, two string arguments | |
Constructing the subroutine | |
sub mysub($$) |
Calling the subroutine |
my($name) = &getName(); # Prints "Bill Clinton" |
Constructing the subroutine |
sub getName |
NOTE: In C++ there are cases where the calling code can "reach into" the function via the returned pointer or reference. This is appearantly not true of passed back scalars. Check out this code:
$GlobalName = "Clinton";
sub getGlobalName
{
return($GlobalName);
}
print "Before: " . &getGlobalName() . "\n";
$ref = \&getGlobalName();
$$ref = "Gore";
print "After: " . &getGlobalName() . "\n";
#All print statements printed "Clinton"I have been unable to hack into a subroutine via its scalar return. If you know of a way it can be done, please let me know, as this would be a horrid violation of encapsulation.
Calling the subroutine |
my($first, $last) = &getFnameLname(); |
Constructing the subroutine |
sub getFnameLname |
Calling the subroutine |
my(%officers) = &getOfficers(); |
Constructing the subroutine |
sub getOfficers |
For readability therefore, on output or input/output arguments it is therefore important to use the output argument as $_[] or @_ throughout the function to let the reader know it's an output argument.
Below is how to change the value of an argument outside the function.
Calling the subroutine |
my($mm, $dd, $yyyy) = ("12", "10", "1998"); # Second print will print 01/01/1999 |
Constructing the subroutine |
sub firstOfNextMonth |
By the way, the above is an excellent example of the advantages of a loosely typed language. Note the implicit conversions between string and integer.
For readability, it is therefore important to immediately assign the input-only arguments to local variables, and only work on the local variables.
Below is how to print changed values without changing the arguments outside
the functions scope.
Calling the subroutine |
my($mm, $dd, $yyyy) = ("12", "10", "1998"); # Before and after will print 12/10/1998. |
Constructing the subroutine |
sub printFirstOfNextMonth |
For readability therefore, on output or input/output arguments it is therefore important to use the output argument as $_[] or @_ throughout the function to let the reader know it's an output argument.
If a member of @_ (in other words, an argument) is a reference to a list, it can be dereferenced and used inside the subroutine.
Here's an example of a listcat() function which appends the second list
to the first. From that point forward the caller will see the new value of
the first argument:
Calling the subroutine |
my(@languages) = ("C","C++","Delphi"); |
Constructing the subroutine |
sub listcat |
By the way, the above is an excellent example of the advantages of a loosely typed language. Note the implicit conversions between string and integer.
For readability, it is therefore important to immediately assign the input-only arguments to local variables, and only work on the local variables.
If a member of @_ (in other words, an argument) is a reference to a list, it can be dereferenced and used inside the subroutine.
Here's an example of an improved listcat() function which appends the
second list to the first without affecting the first outside the subroutine.
Instead, it returns the total string.
Calling the subroutine |
my(@languages) = ("C","C++","Delphi"); |
Constructing the subroutine |
sub listcat |
sub handleArray
{
my(@localArray) = @{shift};
my($element);
foreach $element (@localArray) {print $element . "\n";}
}
&handleArray(\@globalArray);
But once you place the shift command in parens, everything's fine:
sub handleArray
{
my(@localArray) = @{(shift)};
my($element);
foreach $element (@localArray) {print $element . "\n";}
}
&handleArray(\@globalArray);
sub printList(@$) {print @{(shift)}; print shift; print "\n";};The preceding gives some runtime warnings. But the call is missing an arg -- it shouldn't run at all. Instead, use \@ for the list in the prototype, and pass just the list in the call, as follows:
printList(\@globalArray);
sub printList(\@$) {print @{(shift)}; print shift; print "\n";};Now it gives you a "not enough arguments errors, and ends with a compile error, which is what you want. Place an additional scalar in the call so the call matches the prototype, and it runs perfectly:
printList(@globalArray);
sub printList(\@$) {print @{(shift)}; print shift; print "\n";};Remember, using an unbackslashed @ in the prototype defeats the purpose of prototyping. Precede the @ with a backslash. Note that this is also true for passed hashes (%). Unless you have a very good reason to do otherwise, precede all @ and % with backslashes in the prototype.
printList(@globalArray, "Hello World");
For readability therefore, on output or input/output arguments it is therefore important to use the output argument as $_[] or @_ throughout the function to let the reader know it's an output argument.
If a member of @_ (in other words, an argument) is a reference to a hash, it can be dereferenced and used inside the subroutine.
Here's an example of a setGlobals() function which takes an existing %globals
passed in as a reference argument and sets the proper elements. From that
point forward the caller will see the new value of the elements:
Calling the subroutine |
%globals; |
Constructing the subroutine |
sub setGlobals |
For readability, it is therefore important to immediately assign the input-only arguments to local variables, and only work on the local variables.
If a member of @_ (in other words, an argument) is a reference to a list, it can be dereferenced and used inside the subroutine.
Here's an example of an improved listcat() function which appends the
second list to the first without affecting the first outside the subroutine.
Instead, it returns the total string.
Calling the subroutine |
%globals; # now print globals |
Constructing the subroutine |
sub printGlobals |
[slitt@mydesk slitt]$ ./test.pl |
[ Troubleshooters.com | Code Corner | Email Steve Litt ]
Copyright
(C)1998-2003 by Steve Litt -- Legal