I'm trying to figure out how best to write this. I want a subroutine to call another subroutine, and if the return value from that subroutine is true, this subroutine should return that value, otherwise continue. Here's some contenders:
eval { return thatroutine() || die };And then there's a few others that create explicit vars. Any better way to say that?
for (thatroutine()) { return $_ if $_ }
$_ and return $_ for thatroutine();
expresses the notion reasonably clearly, and less verbosely thanreturn $v if my $v = thatroutine();
ormy $v = thatroutine();
if ( $v ) return $v;
While verbose, neigher of these latter two is liable to lead a reader astray.my $v = thatroutine();
return $v if $v;
Re:Maybe returning
merlyn on 2005-03-05T19:32:04
That won't work. The $v isn't declared early enough. There's no way to declare my $v and use it in the same statement.return $v if my $v = thatroutine();Re:Maybe returning
Aristotle on 2005-03-05T22:58:46
But if you use
$_
instead it will work. Along the same lines,$_ = thatroutine() and return $_;which IMO has a better distribution of emphasis.
And a clever for the sake of clever solution:
return $_ for grep $_, thatroutine();Re:Maybe returning
btilly on 2005-03-06T05:13:05
is a very bad solution because it overwrites $_, which is a global variable shared across all packages which some caller far up the chain may be using.$_ = thatroutine() and return $_;
At the very least throw a local in there.Re:Maybe returning
Aristotle on 2005-03-06T19:03:02
I thought of that, actually, but didn't look closely enough at Randall's solutions referring to$_
to notice they merely alias – rather than overwrite – it. My bad.
my $v = thatroutine();
return $v if $v;
But another interesting idiom could be this one:
{ return thatroutine() || last }
Hmmm, awkward — this is one of those things that sounds like it ought to be simple (and clean) to do in Perl, but turns out not do be. I don't like the eval
version, for some reason: perhaps it gives a misleading impression that you expect the called subrountine is likely to die?
How about:
return thatroutine() || do {
# rest of the sub goes here
};
That expresses the logic of what you're up to: return the result either of that function or of running the rest of the code.
I'd probably shunt out the rest of the code to another sub just for symmetry, so you could do:
return thatroutine() || otherroutine($var, \%whatever);
Re:do?
schwern on 2005-03-06T17:30:57
Trouble is nesting.
sub foo {...some code...
return thatroutine() || do {...some more code...
return thisroutine() || do {...some more code...
};
};
}
You're already nested once for the subroutine. Then again for the return exception and then possibly again and again.
Anyhow, I think that's often the wrong emphesis. Often you're kinda aborting the routine, for example.
sub user_info {
my $uid = shift;
my %info = _info_cache($uid);
return %info if keys %info;...get the user info...
}
However, as you mentioned above, that might be better expressed by splitting into three routines:
sub user_info {
my $uid = shift;
my %info = _info_cache($uid) || _get_user_info($uid);
return %info;
}
PLAIN ol' text
schwern on 2005-03-06T17:32:06
Ugg, something went wrong with the formatting on that first example. Should be newlines before all the "...some more code..."Re:PLAIN ol' text
Aristotle on 2005-03-06T19:30:36
Try HTML formatting and using<ecode></ecode>
tags. Automatically indents the code block for you too (less whitespace on copypaste).Re:PLAIN ol' text
schwern on 2005-03-07T17:17:45
Its PLAIN TEXT!Re:do?
Aristotle on 2005-03-06T19:29:04
No, nesting is not a problem.
sub foo {
#...
return
thatroutine()
|| do {
#...
}
|| do {
#...
}
|| do {
#...
}
|| do {
#...
}
|| do {
#...
}
|| do {
#...
};
}PS.: before I added this PS, the site wouldn't let me submit this comment because there was “too much repetition.” WTF?
Re:do?
schwern on 2005-03-07T17:20:20
That code does not do the same thing as I posted. Also it seems like its just a really awful way to write an if/elsif/else.
PS use.perl's odd filters have tripped me before. I think they're vestiges from Slashdot that Pudge hasn't turned off.Re:do?
Aristotle on 2005-03-07T22:07:38
It does do the same thing – the
thisroutine()
call is just the last statement of the firstdo {}
block.The construct doesn't have much in common with an
if
/elsif
chain though. You could write it as one, but it would be very awkward and stilted.Not that I'm a big proponent of this style, mind you.
I don't see that happening with just this line:if the return value from that subroutine is true, this subroutine should return that value, otherwise continue.
The "return" will return the value of "thatroutine()" outside the "eval", but it won't cause the inclosing subroutine to return, if I read the docs correctly. What am I missing?eval { return thatroutine() || die };
# I also dislike single letter variable names;-)
if (my $significant_name = thatroutine()) {
return $significant_name
}
Re:eval correct?
Aristotle on 2005-03-06T19:16:18
(not everyone groks such code)
Sorry, but that's a very poor reason to avoid the short circuit behaviour of boolean ops. Programmers worth their salt must know about it, it such a basic tool, so helpful tool for improving legibility, and so widespread among languages. Without flinching I will go so far as to suggest that those who really cannot figure it out even after explanations, possibly repeated, shouldn't be touching code in the first place.