We have observed strange effects when using $return variable in code like $return = foo($something);. Is $return variable somehow special because of the return name?
According to the Perl documentation, no.
No it is not perl special. But some module my export it and this may provide unexpected behavior.
see This tutorial
There's nothing special about a variable named $return. That said, writing
my $return = foo($something);
return $return;
is not the same as writing
return foo($something);
The first form puts the call to foo() in scalar context. The latter will propagate the context from the caller. This could cause foo() to behave differently. Compare:
sub foo { return localtime }
sub bar { my $x = foo(); return $x }
sub baz { return foo() }
say join ', ', bar(); # Thu May 26 08:24:59 2011
say join ', ', baz(); # 59, 24, 8, 26, 4, 111, 4, 145, 1
This happens because in scalar context, localtime returns the time formatted as a string but in list context it returns a list of values for seconds, minutes, hours, etc.
The concept of context is unique to Perl. To learn more about it, see perlsub.
Related
I have script:
#!/usr/bin/env perl
sub t0 {
return; # We return nothing for ERROR
}
sub t1 {
#z = ();
return #z; # We return array (which maybe empty) for no ERROR
}
In scalar context I can distinguish error and OK status:
my $x1 = t0(); # undef
my $x2 = t1(); # 0
Is there a way to distinguish error and empty array returned from subroutine when it is called in list context?
my #x1 = t0(); # empty list
my #x2 = t1(); # empty list
I am feeling that I need "0E0" (Zero but TRUE) but for list context.
A way to distingish between the empty list and an error in this case would be to return an array reference, e.g.
sub t0 {
return undef; # We return undef for ERROR
}
sub t1 {
#z = ();
return \#z; # We return array (which maybe empty) for no ERROR
}
So basically the functions do not behave differently in list context. They always return a single scalar, which may be undef, which indicates an error, or return a reference to an array.
No.
Return an array reference if successful (which the caller will have to dereference) and undef on failure.
Or throw an exception on failure (which the calling code could catch).
In list context a sub will return a list, even if it only has one element which is undef. If you must return data in a flat list – if it is not feasible to change the callers, say in a large codebase -- you can die on error and eval somewhere at a top level. (As die is an exception it "bubbles.")
The other option for the flat list is rather unappealing, and it still requires changes to callers. You can return an empty list or an undef and then test accordingly -- but see the comment below.
sub test {
# set #z (but not with undef!), $error
return undef if $error;
return #z;
}
my #ret = test();
if (not #ret) {
# empty list
}
else {
foreach my $elem (#ret) {
if (not defined $elem) {
warn "Undefined";
last
}
# ...
}
}
However, as pointed out by ikegami, if #z gets assigned an undef legitimately (without error), for example a variable that happens to be undefined, this code can't tell the difference. So the function would have to be changed further to deal with that.
Python is my main language, but have to maintain a rather large legacy Perl codebase.
I have an odd logic statement that I can't make heads or tails over.
At top, a constant is defined as:
use constant FALSE => 0;
sub thisFunc {
FALSE if ($self->{_thisVar} ne "tif");
...
...
return statement,etc..
}
So I'm reading that as a kinda' fancy, non-standard if-then statement,
that if $thisVar string is not equal to "tif", then FALSE. Huh?
Not something like $that = FALSE, just FALSE.
The form of this statement appears in the file several times.
This codebase is in use, and vetted over the years by very good team,
so I think it is valid and has meaning. "use strict;" is set at top.
Could someone be so kind as to explain what is meant by logic.
I've Google'd it but no joy.
Thanks in advance,
"if" logic in Perl can be constructed in couple of ways:
the obvious one:
if ($flag) { do_something() }
less obvious one:
do_something() if ($flag);
This example shows how exactly behaves that odd "FALSE if" statement - which only meaning is found when it is LAST statement in subroutine:
use strict;
use constant FALSE => 0;
sub thisFunc {
my $arg = shift;
FALSE if ($arg ne "tif");
}
print "return val: ".thisFunc("ble")."\n";
print "return val: ".thisFunc("tif")."\n";
output from running above is:
return val: 0
return val:
It is pointless. I suspect it's suppose to be
return FALSE if $self->{_thisVar} ne "tif";
There is a similar construct that isn't pointless. If the loop condition has side-effects, the following isn't pointless:
1 while some_sub();
aka
while (some_sub()) { }
aka
while (1) {
some_sub()
or last;
}
Practical example:
$ perl -E'$_ = "xaabbx"; 1 while s/ab//; say'
xx
Just wanted to know what was the best way to reserve the value of a variable across multiple calls to the same subroutine . i.e
$someList = &someRoutine(100, 200);
$someList2 = &someRoutine(100, 200);
sub someRoutine {
$someAddition = 0;
foreach $someSum (#_){
$someAddition += $someSum;
}
return $someAddition
}
print $someList;
print $someList2;
Basically, someList should print 300 and someList2 should print 600. How do i make it so that someList2 prints 600? i want $someAddition to be preserved across multiple subroutine calls.
There are several ways to do it. I'll demonstrate two of them:
First, in modern versions of Perl you can use state:
use feature qw/state/;
print someRoutine(100,200), "\n";
print someRoutine(100,200), "\n";
sub someRoutine {
state $someAddition = 0;
foreach my $someSum ( #_ ) {
$someAddition += $someSum;
}
return $someAddition;
}
In this version, the $someAddition variable will be initialized to zero once, and only once. From that point on, the value will be retained between calls.
Another version is using a lexical closure. Here's an example:
my $summer = makeSummer();
print $summer->(100,200), "\n";
print $summer->(100,200), "\n";
sub makeSummer {
my $someAddition = 0;
return sub {
$someAddition += $_ foreach #_;
return $someAddition;
}
}
The second version is a little more complex, but has two advantages. First, you can start a fresh summation simply by calling the makeSummer routine to manufacture a new closure. Second, it will work on any version of Perl 5, not just versions recent enough to have the newer state keyword.
If you are not concerned with initializing the stateful variable before the sub is declared, you can also do this:
my $someAddition;
sub someRoutine {
$someAddition = 0 unless defined $someAddition;
foreach my $someSum( #_ ) {
$someAddition += $someSum;
}
return $someAddition;
}
A fourth method makes use of package globals. I save this one for last because it's the most prone to abuse and mistakes. But here you go;
our $someAddition = 0;
someRoutine(100,200);
print "$someAddition\n";
someRoutine(100,200);
print "$someAddition\n";
sub someRoutine {
$someAddition += $_ foreach #_;
}
In this last version, $someAddition is a package global, and its global scope makes it available both inside and outside of any subroutines living within the same namespace.
I assume you're at least using a variant of Perl 5? It has been bad practice to use ampersands & on subroutine calls since the first version of Perl 5 twenty-two years ago.
It is also vital that you use strict and use warnings at the top of every Perl program, and declare your variables ay their first point of use with my. It is a measure that will uncover many simple coding errors that you can otherwise easily overlook.
Perl variable names should use only lower-case letters, digits, and underscores. Capital letters are reserved for global identifiers such as package names.
By far the simplest and most common way of creating a static variable is just to declare it outside the subroutine. Like this
use strict;
use warnings;
my $some_list = some_routine(100, 200);
my $some_list2 = some_routine(100, 200);
my $some_addition;
sub some_routine {
$some_addition += $_ for #_;
return $some_addition
}
print $some_list, "\n";
print $some_list2, "\n";
output
300
600
If you want to protect the variable from being accessed by any following code other than the subroutine, then just enclose them in braces, like this
{
my $some_addition;
sub some_routine {
$some_addition += $_ for #_;
return $some_addition
}
}
Take a look at Persistent Private Variables in "man perlsub".
How exactly can I pass both scalar variables and array variables to a subroutine in Perl?
my $currVal = 1;
my $currValTwo = 1;
my #currArray = ('one','two','three');
my #currArrayTwo =('one','two','three');
&mysub($currVal, $currValTwo,\#currArray, \#currArrayTwo);
sub mysub() {
# That doesn't work for the array as I only get the first element of the array
my($inVal, $inValTwo, #inArray, #inArrayTwo) = #_;
}
You need to fetch them as references because you've already passed them as references (by using the \ operator):
my($inVal, $inValTwo, $inArray, $inArrayTwo) = #_;
and then use the references as arrays:
#{$inArray}
You pass the arguments as references, so you need to dereference them to use the values. Be careful about whether you want to change the original array or not.
sub mysub {
my($inVal, $inValTwo, $inArray, $inArrayTwo) = #_;
#{$inArrayTwo} = ('five','six','seven');
}
This will change the original #currArrayTwo, which might not be what you want.
sub mysub {
my($inVal, $inValTwo, $inArray, $inArrayTwo) = #_;
my #ATwo = #{$inArrayTwo};
#ATwo = ('five','six','seven');
}
This will only copy the values and leave the original array intact.
Also, you do not need the ampersand in front of the sub name, from perldoc perlsub:
If a subroutine is called using the &
form, the argument list is optional,
and if omitted, no #_ array is set up
for the subroutine: the #_ array at
the time of the call is visible to
subroutine instead. This is an
efficiency mechanism that new users
may wish to avoid.
You do not need empty parens after your sub declaration. Those are used to set up prototypes, which is something you do not need to do, unless you really want to.
So, for example: This is a using statement to search something in an array:
use List::Util qw(first);
This is the sub declaration:
sub GetIndex($$$);
This is the call to the sub (last parameter is: Default index value to give back if not found)
$searchedIndex = GetIndex(\#theArr, "valuesearched", 1);
This is the routine:
sub GetIndex($$$)
{
my $inArray=shift;
my #theArray= #{$inArray};
my $searchedTag= shift;
my $defaultVal= shift;
my $retVal = first { $theArray[$_] eq $searchedTag} 0 .. $#theArray;
if ((! defined $retVal)|| ($retVal<0)||($retVal>#theArray))
{
$retVal = $defaultVal;
}
return $retVal;
}
I have index.pl and subs.pl. When I run the program, the user inserts the date of birth and then it is passed to the getage() subroutine in subs.pl, which has many subroutines.
getage() than implicitly calls another subroutine called validate() which validates the date entered by user.
When I run the index.pl and the user enters the date as 03-04-2005, the following error comes out:
can't modify non-lvalue subroutine call at subs.pl line 85, <> line 1
At 85th line of subs.pl I have:
list(my $val,my #value) = validate($dob);
validate() returns a message and the date($dob) which is sent from getage().
Some code from validate():
sub validate {
my $dob = shift;
my $error;
my #test;
#test = split("-",$dob);
if (!#test) {
$error = "date separator should be - ";
return ($error,#test);
}
...
The solution seems to be:
my ($val, #value) = validate($dob);
based on my intuitive understanding of what that code intended - but I can't be certain till you provide some more context (what does validate() return, what does list() mean?)
If you meant list() as a means to force $val and #value into a list, you merely need to enclose both in parenthesis: ($val, #value) to do that
An lvalue is a variable you can modify. (one that can be on the left side of an assignment, hence the name). In most circumstances, a value returned by a sub is not one you can modify.
In your example, you are attempting exactly that: assigning the return value of validate($dob) to the nonmodifiable return value of list($val, #value).
in line
list(my $val,my #value) = validate($dob);
remove 'list' and it works fine
ie
(my $val,my #value) = validate($dob);
thanks to Kayra and others who answered