1

I have a following statement:

my $iii=0;
while (my @row = sub_undef()) { print $iii++."\n"; }

sub sub_undef {

  if ($error) {
    return "something that will end the while loop";
  } else {
    return 1;
  }
}

In sub_undef() I can have a case (error) when I need to return something that will end while loop. Any ideas how to do it?

-- Dmitry

1
  • 1
    Are you asking this because ordinarily, you return an array? Commented Apr 12, 2013 at 18:41

4 Answers 4

5

An empty array is false1 in boolean context:

return () if $error;

Conveniently, that's what return defaults to, if you don't specify an argument, so you can even just write:

return if $error;

1) A subtlety here is that an empty array evaluates to 0 (that being its length) in scalar context, while an empty list evaluates to undef:

@a = (); $b = @a;  # now $b is 0
$b = ();           # now $b is undef

However, since both 0 and undef are false in boolean context, the difference doesn't really matter here. See perldata for more details.


Edit: If you need to distinguish error conditions from merely running out of data, it's probably better to raise an exception using die for errors and return an empty list for end-of-data. Then your loop would look something like this:

eval {
    while (my @row = sub_undef()) { 
        # do something with @row
    }
};
if ($@) {
    # oops, we got an error, handle it
}

or, using Try::Tiny:

try {
    while (my @row = sub_undef()) { 
        # do something with @row
    }
} catch {
    # oops, we got an error, handle it
};
Sign up to request clarification or add additional context in comments.

11 Comments

I am puzzled. I would have used empty return to signal 'no more stuff to do' whereas if the termination is due to an error, raising an exception seems better to me. After all, is there any way, short of setting a global or something, to figure out what caused the error with the empty return?
@Sinan: I read the OP's question as just asking for a way to "return something that will end while loop." You're right that, if the OP needs to distinguish between error and end-of-data conditions, it would make more sense to raise an exception for the former.
Re "Conveniently, that's what return defaults to", there's no default here. You pass exactly the same thing to return in both cases: an empty list. Do you perhaps think that () creates some kind of variable? That would be incorrect.
Even though return; and return (); are practically equivalent, I like to be explicit to show that the return value has a purpose. I would only use return; when the return value of a subroutine is not used at all.
@Ilmari Karonen, Then it needs fixing because "no arguments" and "empty list" mean the same thing.
|
4

There are a couple of approaches:

Solution 1: If you don't need tor return a row, simply return a false scalar:

sub sub_undef {
    # xxx
    return $error; # Already a true/false scalar
}
while (sub_undef()) { do_stuff(); }

Solution 2: Return an arrayref instead of an array, and false (undef) in case of an error

sub sub_undef {
    # xxx
    return $error ? undef : $rowArrayRef;
}
while (my $row = sub_undef()) { do_stuff(@$row); }

Solution 3: Return a tuple ($status, $rowArrayRef)

sub sub_undef {
    # xxx
    return ($error, $rowArrayRef);
}
while (my ($error, $row) = sub_undef()) { last if $error; do_stuff(@$row); }

Solution 4: Only works if row can NOT be empty unless an error happens based on your business case!

sub sub_undef {
    # xxx
    return $error ? () : @row;
}
while (my @row = sub_undef()) { do_stuff(@row); }

Solution 5: Use exceptions for error handling (Try::Tiny)

# No example worth bothering that won't be covered by Synopsis of the module

3 Comments

I can't get solution 3 to compile; it doesn't seem that a while-condition can contain more than one statement ;-) A for-loop might work better: for(my($error, $row)=sub_undef(); !$error; ($error, $row)=sub_undef()){...}
@amon - oups, you're right. serves me right for trying to needlessly save lines of code.
@ysth That was my first reflex too, but the scope of a lexical variables starts with the next statement. Your !$error refers to a global. (The “comma, ok” idiom from Golang is a nice take on error management, but it doesn't map well to Perl.)
2

return 1 seems to indicate you really mean to return a scalar. If you wrote the loop as follows

while (my ($value) = iter()) {
   ...
}

then you could signal three states:

  • return $value; for a successful return. Because of the parens in my ($value), you could even safely return something false or undefined without causing the loop to exit.

  • return; for when you iterator has been exhausted. This will cause execution to continue after the loop.

  • die "message" to signal an error. This will cause the program to end unless eval BLOCK is used to catch the exception.

Comments

1

Assignment of an empty list will evaluate to false.

  if ($error) {
    return ();
  } else {
    return 1;
  }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.