Stories
Slash Boxes
Comments
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

merlyn (47)

merlyn
  merlyn@stonehenge.com
http://www.stonehenge.com/merlyn/
AOL IM: realmerlyn (Add Buddy, Send Message)
Yahoo! ID: realmerlyn (Add User, Send Message)

PAUSE-ID: MERLYN [cpan.org].
See my home page [stonehenge.com].

Journal of merlyn (47)

Saturday March 05, 2005
11:52 AM

Pondering the best idiom: return if this is true...

[ #23511 ]
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 };

for (thatroutine()) { return $_ if $_ }

$_ and return $_ for thatroutine();

And then there's a few others that create explicit vars. Any better way to say that?

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
 Full
 Abbreviated
 Hidden
More | Login | Reply
Loading... please wait.
  • Are you willing to suffer a temporary variable other than $_? If so, I would think that
    return $v if my $v = thatroutine();
    expresses the notion reasonably clearly, and less verbosely than
    my $v = thatroutine();
    if ( $v ) return $v;
    or
    my $v = thatroutine();
    return $v if $v;
    While verbose, neigher of these latter two is liable to lead a reader astray.
    • return $v if my $v = thatroutine();
      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.
      --
      • Randal L. Schwartz
      • Stonehenge
      • 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();
        • $_ = thatroutine() and return $_;
          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.

          At the very least throw a local in there.
          • 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.
  • I generally use the simple and clear, although verbose:

    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

    • 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
      • Ugg, something went wrong with the formatting on that first example. Should be newlines before all the "...some more code..."
      • No, nesting is not a problem.

        sub foo {
            # ...
            return
                thatroutine()
                || do {
                    # ...
                }
                || do {
                    # ...
                }
                || do {
                    # ...
                }
                || do {
             

        • 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.
          • It does do the same thing – the thisroutine() call is just the last statement of the first do {} 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.

  • Hi,
    In your question you wrote:

    if the return value from that subroutine is true, this subroutine should return that value, otherwise continue.

    I don't see that happening with just this line:

    eval { return thatroutine() || die };

    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?

    Anyway, I usually prefer a simple, readable solution, one that doesn't depend on the short-circuit

    • (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.

  • It would be nice if there was a clear, short idiom to do this but if one cannot be found favor clarity over size.

            my $whatever = foo();
            return $whatever if $whatever;

    I know its a lot of repeating $whatever but its at least clear(ish). Contrast with the other ideas:

            eval { return thatroutine() || die };

            for (thatroutine()) { return $_ if $_ }

            $_ and return $_ for t