tags:

views:

191

answers:

4

So I have an array and a simple function that trims white spaces:

my @ar=("bla ", "ha  1")
sub trim { my $a=shift; $a =~ s/\s+$//; $a}

Now, I want to apply this to an array with the map function. Why can't I do this by just giving the function name like one would do with built in functions?

E.g. You can do

print map(length,@ar)

but you can't do

print map(trim,@ar)

you have to do something like:

print map {trim($_)} @ar
print map(trim($_),@ar)
A: 

Many Perl built-in functions operate on $_ if given no arguments.

If your function did the same, it would work:

my @ar=("bla ", "ha  1");
sub trim { my $s=@_?$_[0]:$_;$s =~ s/\s+$//; $s}
print map(trim,@ar),"\n";

And yes, Perl is kind of gross.

wrang-wrang
You would not have to jump through those hoops if you supply the appropriate prototype for `trim`.
Sinan Ünür
I agree; I learned perl pre-prototypes.
wrang-wrang
Don't use `$a` outside of a sort comparator. No need for `scalar` in the conditional operator: `@_ ? $_[0] : $_`.
Sinan Ünür
I think I'll be using python for my next scripting project :) However, list vs. scalar context is a feature of Perl I enjoy.
wrang-wrang
@wrang-wrang: Feel free to ignore features of Perl you do not like. However, if you are answering a Perl question, statements like *Perl is gross* and *I'll use python* are a little annoying.
Sinan Ünür
Thanks for giving me permission.
wrang-wrang
Your code wouldn't look as gross if you added a bit of whitespace.
Ether
@Ether,ifwhitespaceisn'tmandatoryitisn'tnecessary.
daotoad
It's not necessary, except for the poor humans trying to read it and who would otherwise label it "gross". :)
Ether
Mine is the first correct answer. Keep punishing me, language zealots ;) You're winning!
wrang-wrang
@wrang-wrang: The only zealotry demonstrated in this discussion is coming from you
William Pursell
Oh, snap! Sadly, I'm still using perl for quick stuff, because I know it and am too lazy to switch.
wrang-wrang
+13  A: 

If you are using 5.10 or later, you can specify _ as the prototype for trim. If you are using earlier versions, use Axeman's answer:

As the last character of a prototype, or just before a semicolon, you can use _ in place of $ : if this argument is not provided, $_ will be used instead.

use strict; use warnings;

my @x = ("bla ", "ha  1");

sub trim(_) { my ($x) = @_; $x =~ s!\s+$!!; $x }

print map trim, @x;

Incidentally, don't use $a and $b outside of a sort comparator: They are immune from strict checking.

However, I prefer not to use prototypes for functions I write mainly because their use makes it harder to mentally parse the code. So, I would prefer using:

map trim($_), @x;

See also perldoc perlsub:

This is all very powerful, of course, and should be used only in moderation to make the world a better place.

Sinan Ünür
Does the `_` prototype do anything that `my $x = @_ ? shift : $_` wouldn't?
Michael Carman
@Michael Carman: I do not think so. However, if `trim` is going to operate on `$_` if no argument is supplied, I would prefer to use a prototype rather than adding extra logic to `trim`. Above all, I would prefer neither, and just use `map trim($_), @x`.
Sinan Ünür
@Michael Carman: yes, it works with a lexical $_ in the caller
ysth
Note that the _ prototype is new with 5.10
ysth
Lexical $_ is also new with 5.10 :)
hobbs
@ysth: Thanks, I had forgotten about lexical `$_`. I don't get to use the new features nearly enough. It occurred to me that using the `_` prototype would also allow in-place modification (like `chomp`) without needing to write extra code. (i.e. you could rely on aliasing)
Michael Carman
In sub trim(_) { s!\s+$!! for @_; @_ }, @_ is an alias for the original variables, so you are actually modifying the variables passed in.
runrig
@runrig **Arrgh!** Changed it to mimic the OP's function which operated only on the first argument. Thank you.
Sinan Ünür
I am on 5.8 so this doesn't work for me but sounds like this is the way to do it - thanks.
@naumcho Thank you for accepting my answer.
Sinan Ünür
+5  A: 

The prototype that Sinan talks about is the best current way. But for earlier versions, there is still the old standby:

sub trim { 
    # v-- Here's the quick way to do it.
    my $str = @_ ? $_[0] : $_;
    # That was it.

    $str =~ s/^\s+|\s+$//;
    return $str;
 }

Of course, I have a trim function with more features and handles more arguments and list context, but it doesn't demonstrate the concept as well. The ternary expression is a quick way to do what the '_' prototype character now does.

...BTW, Perl rules!

Axeman
Really good answer since I am on 5.8 - thanks *upvote*; I had to accept Sinan's as a best practice one though - I wish I could accept both.
+2  A: 

my favorite way to optionally use $_ without needing 5.10+ is as follows:

sub trim {
    my ($s) = (@_, $_);
    $s =~ s/\s+$//;
    $s
}

this assigns the first element of @_ to $s if there is one, otherwise uses $_

Eric Strom