Reputation: 1652
refreshing my question.
Sub::Attempts retries once it find the exception (die). For me, I want the sub to retry when sub is returning the false value.
Please let me know what should I change to make it work?
Upvotes: 0
Views: 909
Reputation: 38765
If you have about 60 subs, you could use a wrapper function (idea stolen from HOP)- like this:
sub rpw {
my $f = shift;
my $t = shift;
my $r = &$f(@_);
while ('fail' eq $r && --$t) {
$r = &$f(@_);
}
return $r;
}
to call 'worker' functions (not exactly) like
sub s00 {
my $r = 0.2 > rand() ? 'ok' : 'fail';
print ' in s00 => ', join( '-', @_, $r), "\n";
return $r;
}
sub s01 {
my $r = 0.5 < rand() ? 'ok' : 'fail';
print ' in s01 => ', join( '-', @_, $r), "\n";
return $r;
}
from main code like
print 'from s00 => ', s00(1, 2, 3), "\n";
print 'from s01 => ', s01(qw/a b/), "\n";
print 'from rpw => ', rpw(\&s00, 5, 1, 2, 3), "\n";
print 'from rpw => ', rpw(\&s01, 5, qw/a b/), "\n";
output (not lucky):
in s00 => 1-2-3-fail
from s00 => fail
in s01 => a-b-fail
from s01 => fail
in s00 => 1-2-3-fail
in s00 => 1-2-3-fail
in s00 => 1-2-3-fail
in s00 => 1-2-3-fail
in s00 => 1-2-3-fail
from rpw => fail
in s01 => a-b-fail
in s01 => a-b-ok
from rpw => ok
with a bit of luck:
in s00 => 1-2-3-ok
from s00 => ok
in s01 => a-b-fail
from s01 => fail
in s00 => 1-2-3-fail
in s00 => 1-2-3-fail
in s00 => 1-2-3-fail
in s00 => 1-2-3-ok
from rpw => ok
in s01 => a-b-fail
in s01 => a-b-fail
in s01 => a-b-ok
from rpw => ok
Upvotes: 0
Reputation: 40152
If you want to use Sub::Attempts
, just make a subroutine that modifies the one you have to make it die rather than return false:
sub die_on_failure {
my $name = (caller).'::'.shift;
my $glob = do {no strict 'refs'; \*$name};
my $code = \&$glob;
no warnings 'redefine';
*$glob = sub {
my $ret = &$code;
$ret ? $ret : die "$name failed"
}
}
Then just do:
die_on_failure 'your_sub_name';
before calling:
attempts 'your_sub_name', ...;
Upvotes: 1
Reputation: 13666
Or you could us a simple while loop:
sub retry_before_fail {
my ( $maxtries , $coderef , @args ) = @_ ;
while( $maxtries ) {
# $coderef returns non zero upon success
if( my $result = $coderef->( @args ) ) {
return $result ;
}
$maxtries-- ;
}
# Failure now either return or die
return ;
}
Upvotes: 0
Reputation: 13924
Sounds like you need a loop, of some kind. One way to deal with this would be a simple “all done” flag:
sub foo {
my $success = undef;
until ($success) {
# do something interesting
redo if $something_failed;
# do more things here
++$success; # if it all worked properly
# or, exit early on success:
return $something if $all_is_well;
}
}
Without using a temporary var and an until
loop, you can also use the special form of goto &subroutine
to restart your sub:
sub foo {
# do something interesting
if ($something_failed) {
goto &foo;
}
}
The goto &sub
form will throw out local lexical variables, and start the subroutine over again, but it is susceptible to any changes you may have made to @_
:
sub foo {
my $x = shift @_;
if ($x < 5) {
@_ = ($x + 1);
goto &foo;
}
return $x;
}
print &foo;
__END__
5
The difference between return &foo(@_)
and goto &foo
, is that the goto
version doesn't add to the call stack — a bit like tail recursion optimization.
Upvotes: 0
Reputation: 6882
sub retryBeforeFail {
my $className = shift;
my $attempt = shift;
my $max = shift;
my $success = 0;
... main code here ...
if (!$success && $attempt < $max) {
$attempt++;
return $self->retryBeforeFail($attempt, $max);
} else {
return $success;
}
}
Upvotes: 0