The code I wrote is as below :
#!/usr/bin/perl
my @input = ( "a.txt" , "b.txt" , "c.txt" ) ;
my @output = map { $_ =~ s/\..*$// } @input ;
print @output ;
My intention is to let the file name without the extension stored in the array @output
.
but instead it stores the value returned by s///
rather than the changed file name in @output
, so the result looks like
1
1
1
so what is the correct way to use map
under this situation?
Out of all of those answers, no one simply said that map
returns the result of the last evaluated expression. Whatever you do last is the thing (or things) map
returns. It's just like a subroutine or do
returning the result of their last evaluated expression.
Perl v5.14 adds the non-destructive substitution, which I write about in Use the /r substitution flag to work on a copy. Instead of returning the number of replacements, it returns the modified copy. Use the /r
flag:
my @output = map { s/\..*$//r } @input;
Note that you don't need to use the $_
with the binding operator since that's the default topic.
Ok, first off, you probably meant to have $_ =~ s/\..*$//
— note the missing s
in your example. Also, you probably mean map
not grep
.
Second, that doesn't do what you want. That actually modifies @input
! Inside grep
(and map
, and several other places), $_
is actually aliased to each value. So you're actually changing the value.
Also note that pattern match does not return the matched value; it returns true (if there is a match) or false (if there isn't). That's all the 1's you're seeing.
Instead, do something like this:
my @output = map {
(my $foo = $_) =~ s/\..*$//;
$foo;
} @input ;
The first copies $_
to $foo
, and then modifies $foo
. Then, it returns the modified value (stored in $foo
). You can't use return $foo
, because its a block, not a subroutine.
The problem of $_
aliasing the list's values was already discussed.
But what's more: Your question's title clearly says "map", but your code uses grep, although it looks like it really should use map.
grep
will evaluate each element in the list you provide as a second argument. And in list context it will return a list consisting of those elements of the original list for which your expression returned true.
map
on the other hand uses the expression or block argument to transform the elements of the list argument returning a new list consisting of the transformed arguments of the original.
Thus your problem could be solved with code like this:
@output = map { m/(.+)\.[^\.]+/ ? $1 : $_ } @input;
This will match the part of the file name that is not an extension and return it as a result of the evaluation or return the original name if there is no extension.
You are missing the 's' for substitution.
$_ =~ /\..*$//
should be
$_ =~ s/\..*$//
Also you might be better off to use s/\.[^\.]*$//
as your regular expression to make sure you just remove the extension even when the filename contains a '.' (dot) character.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With