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 ]

Ovid (2709)

Ovid
  (email not shown publicly)
http://publius-ovidius.livejournal.com/
AOL IM: ovidperl (Add Buddy, Send Message)

Stuff with the Perl Foundation. A couple of patches in the Perl core. A few CPAN modules. That about sums it up.

Journal of Ovid (2709)

Sunday April 17, 2005
03:33 PM

Anonymous subroutines vs. if/else

[ #24246 ]

I feel sorry for the poor fools who don't have anonymous subroutines available in their language. I have code that has to produce "Hi" and "Lo" bandwidth links, but we won't always have both available. If we only have one, we print that link. If we have both, we separate the links with a pipe. My ugly code was something like this:

if ($hi) {
   $li_text .= qq'<a href="$url$hi">Hi</a>';
}
if ($hi && $lo) {
    $li_text .= ' | ';
}
if ($lo) {
    $li_text .= qq'<a href="$url$lo">Lo</a>';
}

That's just ugly, so I rewrote it:

my $link = sub { $_[0]? qq'<a href="$url$_[0]">$_[1]</a>' : () };
$li_text .= join ' | ' => $link->($hi, 'Hi'), $link->($lo, 'Lo');

Much nicer and easier to maintain.

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.
  • $li_text .= join " | ", map { $_->[1] ? "<a href='$url$_->[0]">$_->[1]</a>" : () } [Lo => $lo, [Hi => $hi];
    --
    • Randal L. Schwartz
    • Stonehenge
    • And you mean there’s no anonymous function there? *grin*
      • Nope. Try a "return" in that map block. Wow... where'd he go? {grin}
        --
        • Randal L. Schwartz
        • Stonehenge
        • Okay, but it’s still an anonymous code block being passed to map. A language which does not support anonymous function is unlikely to support that idiom too.

          • I don't get the "being passed to" part of your sentence, unless you also say that for (@list) { block } "passes" an "anonymous code block" to "for", which I also cannot support.

            I view the syntax of map as an entity... it has a block that evaluates for its last expression evaluated, and a list of values. You're not "passing anything" to map, unless you view every single function and operator as "passed to".

            --
            • Randal L. Schwartz
            • Stonehenge
            • To me, foreach is a control structure, whereas map is not.

              Per the lisper’s view, map is a function that transforms a list by applying a function to each of its elements.

              Per the pragmatic view, map is documented in perlfunc, whereas foreach is documented in perlsyn.

    • Actually code without typos would have worked better :-) :

      $li_text .= join " | ", map { $_->[1] ? "<a href='$url$_->[0]'>$_->[1]</a>" : () } [Lo => $lo], [Hi => $hi];

  • No need for anonymous functions for this example. A C-ish idiom to attack this:
    for ( [ Hi => $hi ], [ Lo => $lo ] ) {
       $li_text .= qq(<a href="$url$_->[1]">$_->[0]</a> | ) if $_->[1];
    }
    substr $li_text, -3, 3, '';
    (The cleaner version would have the separator and the magic numbers for its length denoted by constants.)
    • Yes, you can do it that way, but I think the examples that both merlyn and I posted were cleaner. Ours were more functional in nature and require less maintenance. For example, let's say that the client wante the delimiter changed in to "<->". You now have two places in your code to change instead of one. That final substr you use is an example of "synthetic" code (code that's used to solve a problem in the programming language rather than solving the actual problem.) The more synthetic code in a

      • Maybe I didn’t make myself clear about the constant. I meant that it would define the separator in a constant and its length in a derived constant so that you’d only need to change one of them. Then all I need to change is the separator constant and the final string shortening call automatically works as intended.

        As far as the functional nature of the idioms you and merlyn wrote is concerned, that’s true, but I didn’t claim otherwise. I did say the code I wrote was C-ish, remember?

        • Also, as an excuse to brush up my C, the C-ish Perl rendered in actual C:

          #define _GNU_SOURCE
          #include <stdlib.h>
          #include <string.h>
          #include <stdio.h>

          #define SEP " | "
          #define SEPLEN ( sizeof( SEP ) - 1 )

          typedef struct {
              char *name;
              char *href;
          } link;

          char *
          links( char *lo, char *hi )
          {
              link the_link[2] = { { "Lo", lo }, { "Hi", hi } };
              char *li_text;
              int i;

              // FIXME: check for malloc success!!
              asp

        • Fair enough about the constants. If one's derived from the other, I can't complain too much :)

    • You know, after the monstrosity I had to write to emulate this in C, I think I’d stick with your ugly initial version and avoid duplication simply by putting the format string in a constant. Although the C version would have more duplication than the link building due to all the malloc scaffolding. Ugh. C’s not the language to do heavy string lifting with (unless you really need to go fast fast fast ).

  • Err, what I forgot to say: obviously, anonymous functions are very useful. You just didn’t pick a good example.
  • join() is one of those functions I always wind up reimplementing when I have to work on proprietary, non-perl code. it's *that* handy ;)
    • That reminds me of one VBScript job I had where I was so irritated by its awful array manipulation functions that I wrote push(), pop(), shift(), unshift() and sort(). My coworkers were mystified by my code, but they admitted that it worked well.