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'Hi'; } if ($hi && $lo) { $li_text .= ' | '; } if ($lo) { $li_text .= qq'Lo'; }
That's just ugly, so I rewrote it:
my $link = sub { $_[0]? qq'$_[1]' : () }; $li_text .= join ' | ' => $link->($hi, 'Hi'), $link->($lo, 'Lo');
Much nicer and easier to maintain.
$li_text.= join " | ", map { $_->[1] ? "<a href='$url$_->[0]">$_->[1]</a>" : () } [Lo => $lo, [Hi => $hi];
Re:A good map would have worked there
Aristotle on 2005-04-18T08:23:02
And you mean there’s no anonymous function there? *grin*Re:A good map would have worked there
merlyn on 2005-04-18T12:58:05
Nope. Try a "return" in that map block. Wow... where'd he go? {grin}Re:A good map would have worked there
Aristotle on 2005-04-18T23:09:04
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.Re:A good map would have worked there
merlyn on 2005-04-19T01:25:05
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".
Re:
Aristotle on 2005-04-20T11:53:17
To me,
foreach
is a control structure, whereasmap
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 inperlfunc
, whereasforeach
is documented inperlsyn
.Re:A good map would have worked there
rats on 2005-04-19T03:07:05
Actually code without typos would have worked better:-) :
$li_text
.= join " | ", map { $_->[1] ? "<a href='$url$_->[0]'>$_->[1]</a>" : () } [Lo => $lo], [Hi => $hi];
(The cleaner version would have the separator and the magic numbers for its length denoted by constants.)for ( [ Hi => $hi ], [ Lo => $lo ] ) {
$li_text.= qq(<a href="$url$_->[1]">$_->[0]</a> | ) if $_->[1];
}
substr $li_text, -3, 3, '';
Re:
Ovid on 2005-04-18T16:44:25
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 an application, the more bugs one is likely to have.
And a cleaner version would not have both a separator and magic numbers. One or the other, please.
Re:
Aristotle on 2005-04-18T23:41:54
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? It’s not the way I would solve this problem in Perl – I’d definitely go for some variation on the
join
theme. But if I were working in a language with no support for anonymous functions, the simplest solution would likely look something like the Perl I wrote here.Re:
Aristotle on 2005-04-18T23:48:38
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!!
asprintf( &li_text, "" );// make sure there's something on the heap
for( i = 0; i < ( sizeof( the_link ) / sizeof( the_link[0] ) ) ; ++i ) {
char *new_text;
if( ! ( the_link[i].href && strlen( the_link[i].href ) ) ) continue;
asprintf( &new_text, "%s<a href=\"%s\">%s</a>" SEP, li_text, the_link[i].href, the_link[i].name );
free( li_text );
li_text = new_text;
}
li_text[ strlen( li_text ) - SEPLEN ] = '\0';
return li_text;
}Certainly drives home the point that languages with first-class lists and automatic memory management make the job much easier, huh? If you find a better example, you can certainly show that anonymous functions make things much easier too.
:) Re:
Ovid on 2005-04-18T23:50:16
Fair enough about the constants. If one's derived from the other, I can't complain too much
:) Re:
Aristotle on 2005-04-18T23:56:27
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 ).
Re:don't forget join()!!
Ovid on 2005-04-19T01:03:49
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.