How can Perl get its caller back?

Possible duplicate:
Is it possible for a Perl routine to force a caller to return?

I want to write a subroutine that forces the caller to return under certain conditions. This is intended to be used as a shortcut to validate function input. What I still have:

sub needs($$) { my ($condition, $message) = @_; if (not $condition) { print "$message\n"; # would like to return from the *parent* here } return $condition; } sub run_find { my $arg = shift @_; needs $arg, "arg required" or return; needs exists $lang{$arg}, "No such language: $arg" or return; # etc. } 

The advantage of returning from the caller to needs will be to avoid having to write duplicate or return inside run_find and similar functions.

+1
source share
4 answers

I think you are focused on what's wrong here. I do such things with Data :: Constraint, Brick , etc. And talk about it in Mastering Perl. With a little ingenuity and thought about the structure of your program and the dynamic functions that Perl has, you do not need such a regulated procedural approach.

However, the first thing you need to find out is what you really want to know in this calling routine. If you just want to know yes or no, it's pretty easy.

The problem with your needs is that you think of invoking it once for each condition, which forces you to use needs to control the flow of the program. This is the wrong way. needs just to give you an answer. The task is not to change the state of the program. This becomes less useful if you abuse it, because some other calling routine may want to continue, even if needs returns false. Call once and let him return once. The calling routine uses the return value to decide what it should do.

The basic structure includes the table that you pass needs . This is your verification profile.

  sub run_find {
     my $ arg = shift @_;
     return unless needs [
         [sub {$ arg}, "arg required"],
         [sub {exists $ lang {$ arg}}, "No such language: $ arg"],
         ];
     }
     ...
     }

You create a table for any of your requirements. In needs you just process the table:

 sub needs($$) { my ($table) = @_; foreach $test ( @$table ) { my( $sub, $message ) = @$test; unless( $sub->(...) ) { print $message; return } } return 1; } 

Now, the really cool thing with this approach is that you don't need to know the table in advance. You can pull this out of the configuration or in some other way. It also means that you can dynamically modify the table. Now your code is shortened a bit:

  sub run_find {
     my $ arg = shift @_;
     return unless needs ($ validators {run_find});
     ...
     }

You will continue with this. In Mastering Perl, I show a couple of solutions that completely remove this from the code and transfer it to the configuration file. That is, you can change business rules without changing the code.

Remember that almost at any time when you enter the same sequence of characters, you are probably mistaken. :)

+4
source

It looks like you are reinventing exception handling.

The needs function should not magically display its parent element and interrupt the flow of parental control - these are bad manners. What if you add additional functions to the call chain and you need to return two or even three functions back? How can you determine this programmatically? Will the caller expect his or her function to return earlier? You should follow the principle of least surprise if you want to avoid mistakes - and that means using exceptions to indicate a problem, and let the caller decide how to deal with it:

 use Carp; use Try::Tiny; sub run_find { my $arg = shift; defined $arg or croak "arg required"; exists $lang{$arg} or croak "no such language: $arg"; ... } sub parent { try { run_find('foo') } catch { print $@ ; } } 

Any code inside the try block is special: if something dies, the exception is caught and stored in $@ . In this case, a catch block is executed, which prints an error in STDOUT, and the control flow continues as usual.

Disclaimer: Perl exception handling is a pain. I recommend Try :: Tiny , which protects against many common errors (and provides familiar try / catch semantics) and Exception :: Class , to quickly create exception objects so that you can distinguish between Perl and native errors.

It might be easier for you to use the CPAN module to check the arguments, for example Params :: Validate .

+4
source

You might want to look at a similar recent kinopico question: Is it possible for the Perl routine to force a caller back?

Summary for this: the best solution is to use exceptions (die / eval, Try :: Tiny, etc.). You are also using GOTO and possibly Continuation :: Escape

+1
source

It makes no sense to do it this way; ironically, ya doesn't need needs .

That's why.

  • run_find poorly written. If your first condition is true, you will never test the second, since you already have return ed.
  • warn and die functions will in any case provide you with printing and / or exit from the mode.

This is how I will write your run_find sub if you want to complete the execution if your argument failed (renamed it well_defined ):

 sub well_defined { my $arg = shift; $arg or die "arg required"; exists $lang{$arg} or die "no such language: $arg"; return 1; } 

At the same time, there should be a way to return 0 and warn , but I need to play a little with it.

run_find can also be written to return 0 and the corresponding warn message if conditions are not met, and return 1 if they are (renamed well_defined ).

 sub well_defined { my $arg = shift; $arg or warn "arg required" and return 0; exists $lang{$arg} or warn "no such language: $arg" and return 0; return 1; } 

This allows you to perform logical behavior, as shown below:

 perform_calculation $arg if well_defined $arg; # executes only if well-defined 
0
source

All Articles