Is there a way to distinguish error and empty array returned from subroutine when it is called in list context? - perl

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.

Related

How to check whether file content is empty , by traversing the array of filenames

i have a array of filenames , and i have to traverse this array and check whether the content of ALL the files is empty.
Here is the code
foreach my $reportFile (sort { getDateInName($b) <=> getDateInName($a)} #ReportFiles)
{
my #fileData = readFile($reportFile);
if(!#fileData)
{
outputLog("FAIL: File Doesnt Contain Any Data.");
return;
}
}
But in the code above, iam returning even if a single file is empty,
I would like to know how can we check whether ALL the content of ALL files is empty and then return.
So I would like to return only if none of the files in the array has content.
Even if one file has content i wouldnt return
Thanks
Use List::Util::all:
Similar to any, except that it requires all elements of the #list to make the BLOCK return true. If any element returns false, then it returns false. If the BLOCK never returns false or the #list was empty then it returns true.
use List::Util 'all';
sub check_files {
...
warn( "All files empty" ), return
if all { -z } #_;
...
}
or
sub are_all_empty { all { -z } #_ }
First off, if you only want to check if a file is empty, you should not try to read it. This might be dangerous as you might ending up reading a huge file in memory. You can test for a file being empty like that if (-s $reportFile) {...}. Secondly, to fix the problem that you have with returning if any file is empty, you need to invert the logic of you code, i.e. you must check if any file is not empty. This is because of the following logical equivalence: saying that "all the files are empty" is the same as "no file is nonempty". Putting it all together, you get something like that:
sub all_empty {
foreach my $reportFile (sort { getDateInFileName($b) <=> getDateInName($a)} #ReportFiles)
{
if (-s $reportFile) {
return 0;
}
}
return 1;
}
Try to have a boolean flag, which is set to one if u have content in atleast one file, so that you do not have to traverse through the array and return as soon as u find a file with content
And if there is no content in the file, go to next iteration to check if the content is avaialble.
And come out of loop if content is present in any of the file
my $isFileEmpty = 0 ;
foreach my $reportFile (sort { getDateInFileName($b) <=> getDateInName($a)} #ReportFiles)
{
my #fileData = readFile($reportFile);
if(#fileData) {
$isFileEmpty = 1;
last;
}
else {
next ;
}
}
if($isFileEmpty eq 0)
{
return;
}
PS : Do you have content available in most of the cases ?

Why is this defined value not recognized as a package or object reference?

I have the code below:
my $content = $response->decoded_content((charset => 'UTF-8'));
my $feed = XML::Feed->parse(\$content) || $logger->error("When retrieving $URL: ", XML::Feed->errstr);
if (defined $feed) {
for my $entry ($feed->entries) {
#DO SOMETHING
}
}
For some site, XML::FEED saying that it can't detect the feed type. This is something I have to look at but this is not my question at the moment.
This sample code is inside a while loop has I'm retrieving different RSS and I would like to have the script running even when some URLs failed.
The defined function seems to not work as I get the error message:
Can't call method "entries" without a package or object reference
Can someone tell me what is the right way to handle the test?
You first have to check the value of $feed.
The error message you describe is obvious: $feed is not a package / object reference, but it can be a simple hash for instance. So it's defined.
Add my favourite debugging line right in front of if(defined):
warn Data::Dumper->new([ $feed ],[ '*feed' ])->Sortkeys(1)->Dump();use Data::Dumper;
and you'll see the value in a nice way.
Without testing I'd say that $feed contains the result of your logger, which might be 1 or 0 or something like that, because you set the value of $feed to XML::Feed->parse, and if this is not successful (undefined) it's the result of $logger->error.
You'd better write it like:
my $feed = XML::Feed->parse(\$content);
if (defined $feed) {
for my $entry ($feed->entries) {
#DO SOMETHING
}
}
else {
$logger->error("When retrieving $URL: ", XML::Feed->errstr);
}
because parse is said to return an object, and I guess it returns undef on error.
The error message means what it says: $feed is neither a package nor an object reference. It passes the defined test because there are many defined values which are neither packages nor object references.
In this particular case, you're seeing this error because you are misuing ||:
my $feed = XML::Feed->parse(\$content) || $logger->error("When retrieving $URL: ", XML::Feed->errstr);
If the parse call should fail and return undef, this evaluates to
my $feed = ( undef || $logger->error("When retrieving $URL: ", XML::Feed->errstr) );
which evaluates to
my $feed = $logger->error("When retrieving $URL: ", XML::Feed->errstr);
. The return value of $logger->error is unknown to me, but presumably it is neither a package nor an object reference. And if it were one, it probably would be the wrong one to put in a variable named $feed.
The documentation for XML::Feed mentions parsing with a construct like
my $feed = XML::Feed->parse(URI->new('http://example.com/atom.xml'))
or die XML::Feed->errstr;
This is not the same thing. Their respective precedence rules make || and or suitable for different applications; specifically, you should only use || when you want the value on the right-hand side for something. Do not use it only for the short-circuit side effect.
You can solve this by replacing the || with or to get the right evaluation order. While you are there, you probably should also eliminate the redundant defined test.

Perl subroutine array and scalar variable parameters

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;
}

Why does Perl complain "can't modify non-lvalue subroutine call"?

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

How can I cleanly handle error checking in Perl?

I have a Perl routine that manages error checking. There are about 10 different checks and some are nested, based on prior success. These are typically not exceptional cases where I would need to croak/die. Also, once an error occurs, there's no point in running through the rest of the checks.
However, I can't seem to think of a neat way to solve this issue except by using something analogous to the following horrid hack:
sub lots_of_checks
{
if(failcond)
{
goto failstate:
}
elsif(failcond2)
{
goto failstate;
}
#This continues on and on until...
return 1; #O happy day!
failstate:
return 0; #Dead...
}
What I would prefer to be able to do would be something like so:
do
{
if(failcond)
{
last;
}
#...
};
An empty return statement is a better way of returning false from a Perl sub than returning 0. The latter value will actually be true in list context:
sub lots_of_checks {
return if fail_condition_1;
return if fail_condition_2;
# ...
return 1;
}
Perhaps you want to have a look at the following articles about exception handling in perl5:
perl.com: Object Oriented Exception Handling in Perl
perlfoundation.com: Exception Handling in Perl
You absolutely can do what you prefer.
Check: {
last Check
if failcond1;
last Check
if failcond2;
success();
}
Why would you not use exceptions? Any case where the normal flow of the code should not be followed is an exception. Using "return" or "goto" is really the same thing, just more "not what you want".
(What you really want are continuations, which "return", "goto", "last", and "throw" are all special cases of. While Perl does not have full continuations, we do have escape continuations; see http://metacpan.org/pod/Continuation::Escape)
In your code example, you write:
do
{
if(failcond)
{
last;
}
#...
};
This is probably the same as:
eval {
if(failcond){
die 'failcond';
}
}
If you want to be tricky and ignore other exceptions:
my $magic = [];
eval {
if(failcond){
die $magic;
}
}
if ($# != $magic) {
die; # rethrow
}
Or, you can use the Continuation::Escape module mentioned above. But
there is no reason to ignore exceptions; it is perfectly acceptable
to use them this way.
Given your example, I'd write it this way:
sub lots_of_checks {
local $_ = shift; # You can use 'my' here in 5.10+
return if /condition1/;
return if /condition2/;
# etc.
return 1;
}
Note the bare return instead of return 0. This is usually better because it respects context; the value will be undef in scalar context and () (the empty list) in list context.
If you want to hold to a single-exit point (which is slightly un-Perlish), you can do it without resorting to goto. As the documentation for last states:
... a block by itself is semantically identical to a loop that executes once.
Thus "last" can be used to effect an early exit out of such a block.
sub lots_of_checks {
local $_ = shift;
my $all_clear;
{
last if /condition1/;
last if /condition2/;
# ...
$all_clear = 1; # only set if all checks pass
}
return unless $all_clear;
return 1;
}
If you want to keep your single in/single out structure, you can modify the other suggestions slightly to get:
sub lots_of_checks
{
goto failstate if failcond1;
goto failstate if failcond2;
# This continues on and on until...
return 1; # O happy day!
failstate:
# Any clean up code here.
return; # Dead...
}
IMO, Perl's use of the statement modifier form "return if EXPR" makes guard clauses more readable than they are in C. When you first see the line, you know that you have a guard clause. This feature is often denigrated, but in this case I am quite fond of it.
Using the goto with the statement modifier retains the clarity, and reduces clutter, while it preserves your single exit code style. I've used this form when I had complex clean up to do after failing validation for a routine.