tags:

views:

204

answers:

6

I want to validate a condition before doing the next step, but only raise a warning and skip the current value instead of die-ing.

How do I rewrite validate_me() subroutine without returning any value?

(Update) please note that the following code works as expected, it just that I want something else instead of returning 1 or 0 from validate_me() that still allow the code to behave the same.

foreach my $file ( @files ) {
    validate_me($foo, $bar, $baz) or next;
    do_something();
}  

sub validate_me {
    my ($foo, $bar) = @_;

    if ( $foo > $bar ) {
        carp("Something goes awol");
        return 0;
    }

    if ( $bar != $baz ) {
        carp("Odd");
        return 0;
    }

    return 1; # success
}

Thanks.

A: 

Update:

I want something else instead of returning 1 or 0 from validate_me() that still allow the code to behave the same.

Is this what you mean? See eval.

my @files = qw( a b c d e f );

FILES:
foreach my $file ( @files ) {
    eval { validate_me($file) };
    if (my $ex = $@) {
        warn "'$file' failed validation: $ex";
        next FILES;
    }
    print "$file passed\n";
}

sub validate_me {
    my ($file) = @_;
    die "Invalid filename\n" if 0.5 > rand;
    return; # explicitly return nothing
}

Now, the sane thing to do would be to refactor validate_me into its components:

for my $file ( @files ) {
    if ( is_something_gone_awol($file, $mile) ) {
        warn "Something has gone awol: '$file' and '$mile'\n";
    }
    elsif ( is_something_odd($file, $smile) ) {
        warn "Something is odd: '$file' and '$smile'\n";
    }
    else {
        do_something($file);
    }
}

sub is_something_gone_awol {
    my ($foo, $bar) = 0;
    return $foo gt $bar;
}

sub is_something_odd {
    my ($bar, $baz) = @_;
    return $bar ne $baz;
}
Sinan Ünür
I am :) may be I did not express my question clearly. Your solution is still returning 1 from validate_me(), whilst what I'm trying to achieve is to have the code behaves the same as per my example but without checking the return value from validate_me().
est
Incidentally, returning undef without die/eval here would be the same.
jrockway
+2  A: 

First, there's no reason to use a label in your loop, a next; by itself will automatically do the next iteration of the enclosing loop.

As to your question, I'm a little confused about what you're trying to accomplish. Your code as written will return zero, which is a false value, from validate_me if any of your tests fail, and that should cause the next iteration of the loop. There's nothing in validate_me that would cause anything to die.

friedo
I don't want it to die, just raise warning.
est
One of Damian's better ideas from PBP is to label any loop you are going to jump around in with next / last / repeat. I think this is a good idea, plus you get documentation for the loop and every exit point for free.
jrockway
@est, NOTHING in the code you posted would cause anything to die. `carp` emits a warning. Nothing else could cause a fatal error. Try updating your question to be more specific about what you're trying to accomplish.
friedo
There's plenty of reason to label a loop. I do it even in not-nested loops to signal what I'm iterating through.
brian d foy
A: 

You can return undef or just return.

mobrule
The two are different. You need to choose one.
Sinan Ünür
Never return undef. That gives you a list of one item, which is almost never what you want.
brian d foy
And the difference will show up if you do `@a = validate_me()` (or, in a common source of bugs with CGI::param, something like `%a = ( 'valid' => validate_me(), ... )`
ysth
@brian d foy: nonsense. that is quite often what you want, unless your subroutine has some special meaning in list context. If the normal use is scalar context, much better to return undef even in list context.
ysth
Consider "`my $foo = do { return undef }; say 'false' unless $foo` ==> false;" and "`my @foo = do { return undef }; say 'true' if @foo` ==> 'true'". This is a confusing API. I would agree with brian and say to never "return undef".
jrockway
I must second brian here too to never return undef. I had been beaten with it several times in the past when my subroutine returns undef in list context.
est
In scalar context, there is no difference between `return undef` and `return`. In list context, `return undef` will return a true value. So there is no downside to *never* returning undef explicitly.
Ether
I will never `return undef` again.
mobrule
There most certainly is a downside. If a sub returns some scalar on success, it shouldn't make an exception and return nothing on failure. Among other cases, you get bugs where people are surprised by $foo in `$foo = bar()` being different than $foo{'bar'} in `%foo = ( 'bar' => bar(), 'baz' => baz() );` (not to mention $foo{'baz'} disappearing).
ysth
I'm guessing jrockway meant eval, not do, there. And his point seems addressed to a subroutine that is expected to return a list, where I would agree that return undef would be wrong. But that's a very different case than a subroutine that is expected to return a scalar.
ysth
Ah yes. I always assume `do` works like `(sub { ... })->()`, but of course it does not.
jrockway
You can still return a false scalar, rather than undef. e.g. one might return the result of the spaceship operator or a boolean expression: `return $a < $b;` (which would resolve ysth's example of stuffing return values into a hash). I just wouldn't ever explicitly return **undef**.
Ether
+5  A: 

Check the documentation for return. If you want to return nothing, don't give return an argument:

 return;
brian d foy
I knew that, but how do I rewrite my code snippet above without validate_me() returning 1 or 0?
est
Well, where you have return, use the line of code I just gave you.
brian d foy
@brian: blindly replacing all the lines into "return", including "return 1" in the end will cause the code to break of course?
est
If you would like be more verbose you can explicitly write return (); to say: "Really just return nothing".
Hynek -Pichi- Vychodil
@est: I don't know what you are afraid of. In the spot in the code where you want to return nothing, you have your answer. I think you are making this too tough on yourself.
brian d foy
A: 

return; will just end the function, no questions asked.

But in case you're wondering, you can also return values by using pointers/references passed into your function as parameters.

void foo(int input, outStruct *output) {
...
output = new outStruct(...); //for example;
return;
};

EDIT I'm a moron, who can't read tags/code. The example is in C/C++, not Perl. OOps.

Lanissum
Did you miss the **[perl]** tag?
Sinan Ünür
His point is still valid, even if the example is in the wrong language -- a function can update the contents of references that are passed in as arguments, and so pass values back to the caller that way.
Ether
Can, sure... but there's a reason why C programs crash so often...
jrockway
+1  A: 

throw an exception!

David