Re: Confessions of a back-alley map abuser
by Juerd (Abbot) on Dec 18, 2004 at 23:09 UTC
|
By its design, map is intended for use with arrays, not scalars.
lists, not arrays!
Arrays are those things that have @. And even then, an array used in list context evaluates to a list of its elements.
@foo # array
@Foo::bar # array
@$foo # array
@{ $bar } # array
@{ $blah{blah}[1] } # array
@list # array with a confusing name
$array # scalar (possibly a reference)
[ ... ] # scalar (reference to anonymous array)
\@array # scalar (reference to named array)
*foo{ARRAY} # scalar (reference to the package global @foo)
print @array # list (print "gets" a list, not an array,
# because @array is in list context)
return @array # list! (again, array in list context)
\(1, 2, 3) # list of references, not a reference to a list
@foo = # array
map { $_ + 1 } # list
map { s/\n//g } # list
@bar # list! (array in list context)
Subs get and return lists. List operators (like map) take lists. Arrays can be referenced, lists cannot. "Array context" exists, but only syntax-wise (\@ prototype or special syntax). Most of the time "list context" is what you really mean. Oh, and arrays can have names, lists cannot.
Please, learn the difference. It's confusing enough already even if you use the *correct* words.
| [reply] [d/l] |
A reply falls below the community's threshold of quality. You may see it by logging in. |
Re: Confessions of a back-alley map abuser
by Zaxo (Archbishop) on Dec 18, 2004 at 19:39 UTC
|
You've put your chaining at a disadvantage in the ugliness department by illustrating it with mutators of $_. This technique shines in combination with other list operators like grep and sort, and when wrapped in a subroutine so that it acts on a copy of @_.
sub quux {
my @args = @_;
map { foo $_ }
map { bar $_ }
map { baz $_ } @args;
}
The real alternative is nested sub calls. While those look messy in C, with all the parens to be balanced, they have an equally nice expression in Perl,
my $quux = foo bar baz $_;
One of the better reasons to use map in this way is the aliasing of localized $_ to each argument in turn. That is not a big advantage in your actions on a list of one element, but is a nice shorthand when operations act on $_ by default. You missed an opportunity to use that in the map { uc } stage.
Of course, this is all extremely powerful when acting on lists. The Schwartzian Transform is on anybody's list of "Best Dressed Perl".
| [reply] [d/l] [select] |
|
The Schwartzian Transform is on anybody's list of "Best Dressed Perl".
Heh. Not the VP of Technology of a former workplace of mine who denounced an instance of the Schwartzian Transform in my code as "unreadable and unmaintainable." (It was commented as an ST, so any who didn't know it could look it up, but, of course, he already knew it was bad so he could save himself the bother of learning something new.)
Of course, his idea of good Perl was Perl trying to look like Java...
| [reply] |
Re: Confessions of a back-alley map abuser
by Sidhekin (Priest) on Dec 18, 2004 at 18:48 UTC
|
I disagree that ugly0 and ugly1 really are ugly. ugly0 is just list assignment syntax. ugly1 is just recognition that we need a return value from the map block. And in my mind, the only abuse of map, is when you use it in void context or throw away the return value(s).
Having said that, your example does not so much look like a pipeline to me. It looks more like a succession of operations:
my $sBegin = "hello world";
my $sEnd = do {
local $_ = $sBegin;
s/hello/goodbye/g;
s/world/todo el mondo/;
uc;
};
print $sEnd;
When the only tool you have is map, every problem looks like a pipeline?
print "Just another Perl ${\(trickster and hacker)},"
The Sidhekin proves Sidhe did it!
| [reply] [d/l] [select] |
|
I like to use "for" for that kind of thing:
my $sBegin = "hello world";
my $sEnd = $sBegin;
for ($sEnd) {
s/hello/goodbye/g;
s/world/todo el mondo/;
uc;
};
print $sEnd;
| [reply] [d/l] |
|
I like to use "for" for that kind of thing
Me too, but I have found do{} is more readable. For one thing, it saves you from your bug: Your version throws away the uppercase string and so prints just "goodbye todo el mondo". My version would look like this:
my $sBegin = "hello world";
my $sEnd;
for ($sEnd = $sBegin) {
s/hello/goodbye/g;
s/world/todo el mondo/;
$_ = uc;
};
print $sEnd;
TIMTOWTDI, but I think do{} is the more readable here.
Update: I trust you. No problem. But the bug still illustrates how the void context makes the for loop slightly less readable here. Such a bug (update: or the more likely opposite bug) could not as easily occur in the do{} block.
print "Just another Perl ${\(trickster and hacker)},"
The Sidhekin proves Sidhe did it!
| [reply] [d/l] [select] |
|
Re: Confessions of a back-alley map abuser
by hv (Prior) on Dec 18, 2004 at 18:41 UTC
|
map {...} "$sBegin"; # quote to duplicate string
Hugo | [reply] [d/l] [select] |
|
| [reply] [d/l] |
Re: Confessions of a back-alley map abuser
by Aristotle (Chancellor) on Dec 21, 2004 at 03:41 UTC
|
sub{$sBegin}->() can be written @{ [ $sBegin ] }.
The one case were I frequently find myself “abusing” map in this fashion is to qr// a string built with function calls, usually a join with a bar:
my( $rx ) = map qr/($_)/, join '|', @alternative;
The other way to express this in condensed form is
my( $rx ) = do { local $" = '|'; qr/(@alternative)/ };
I'm not sure why I prefer the former (they're both kind of kludgy), but I do. :-)
Makeshifts last the longest. | [reply] [d/l] [select] |
Re: Confessions of a back-alley map abuser
by ccn (Vicar) on Dec 18, 2004 at 18:51 UTC
|
| [reply] |
Re: Confessions of a back-alley map abuser
by Anonymous Monk on Dec 20, 2004 at 16:47 UTC
|
I like to use local $_ = $_; to prevent the arguments from being modified:
($sEnd) = map {uc}
map {s/world/todo el mundo/; $_}
map {local $_ = $_; s/hello/goodbye/; $_}
$sBegin;
# Or:
($sEnd) = map {uc}
map {s/world/todo el mundo/; $_}
map {s/hello/goodbye/; $_}
map {local $_ = $_}
$sBegin;
| [reply] [d/l] [select] |