我写的代码如下:
#!/usr/bin/perl
my @input = ( "a.txt" , "b.txt" , "c.txt" ) ;
my @output = map { $_ =~ s/\..*$// } @input ;
print @output ;
我的目的是让没有扩展名的文件名存储在数组@output
中。
但它存储s///
返回的值而不是@output
中更改的文件名,因此结果如下所示
1
1
1
那么在这种情况下使用map
的正确方法是什么?
答案 0 :(得分:16)
在所有这些答案中,没有人简单地说map
返回最后一个评估表达式的结果。无论你最后做什么都是map
返回的东西。它就像一个子程序或do
返回结果的最后一个评估表达式。
Perl v5.14添加了非破坏性替换,我在Use the /r substitution flag to work on a copy中写道。它不返回替换次数,而是返回修改后的副本。使用/r
标志:
my @output = map { s/\..*$//r } @input;
请注意,您不需要将$_
与绑定运算符一起使用,因为这是默认主题。
答案 1 :(得分:15)
好的,首先,您可能需要$_ =~ s/\..*$//
- 请注意示例中缺少的s
。此外,您可能意味着map
而不是grep
。
其次,这不符合你的要求。这实际上修改了@input
!在grep
(和map
以及其他几个地方)中,$_
实际上是每个值的别名。所以你实际上是在改变价值。
另请注意,模式匹配不会返回匹配的值;它返回true(如果有匹配)或false(如果没有)。这就是你所看到的所有1个。
相反,做这样的事情:
my @output = map {
(my $foo = $_) =~ s/\..*$//;
$foo;
} @input ;
首先将$_
复制到$foo
,然后修改$foo
。然后,它返回修改后的值(存储在$foo
中)。你不能使用return $foo
,因为它是一个块,而不是一个子程序。
答案 2 :(得分:7)
已经讨论过$_
别名列表值的问题。
但更重要的是:你的问题标题清楚地写着“地图”,但是你的代码使用了grep,虽然它看起来确实应该使用map。
grep
将评估您提供的列表中的每个元素作为第二个参数。在列表上下文中,它将返回一个列表,该列表包含表达式返回true的原始列表的那些元素。
map
使用表达式或块参数来转换list参数的元素,返回一个新的列表,该列表由原始的转换参数组成。
因此,您的问题可以通过以下代码解决:
@output = map { m/(.+)\.[^\.]+/ ? $1 : $_ } @input;
这将匹配不是扩展名的文件名部分,并作为评估结果返回,如果没有扩展名,则返回原始名称。
答案 3 :(得分:2)
你错过了's'代替。
$_ =~ /\..*$//
应该是
$_ =~ s/\..*$//
另外,您可能最好使用s/\.[^\.]*$//
作为正则表达式,以确保即使文件名中包含“。”也只是删除了扩展名。 (点)字符。
答案 4 :(得分:1)
derobert向您展示了将@input
映射到@output
的正确方法。
但是,我建议使用File::Basename
:
#!/usr/bin/perl
use strict;
use warnings;
use File::Basename;
my @input = qw( a.1.txt b.txt c.txt );
my @output = map { scalar fileparse($_, qr/\.[^.]*/) } @input ;
use Data::Dumper;
print Dumper \@output;
<强>输出:强>
C:\Temp> h $VAR1 = [ 'a.1', 'b', 'c' ];
答案 5 :(得分:1)
如上所述,s ///返回执行的替换次数,map返回每次迭代计算的最后一个表达式,因此你的map返回全1。实现目标的一种方法是:
s/\..*$// for my @output = @input;
另一种方法是使用Algorithm::Loops
中的过滤器答案 6 :(得分:0)
您的代码示例在匹配运算符中缺少s
。除此之外,它对我来说很好:
$, = "\n";
my @input = ( "a.txt" , "b.txt" , "c.txt" );
my @output = grep { $_ =~ s/\..*$// } @input;
print @output;
输出是:
a b c
答案 7 :(得分:0)
问题:the s/../.../
operator和Perl's map
都是必不可少的,期望您想要修改每个输入项; Perl事实上没有内置函数map
,它可以在不修改输入的情况下产生结果。
使用s
时,选项是附加r
修饰符:
#!/usr/bin/perl
my @input = ( "a.txt" , "b.txt" , "c.txt" ) ;
my @output = map { s/\..*$//r } @input ;
print join(' ', @output), "\n";
一般解决方案(由derobert建议)是使用List::MoreUtils::apply:
#!/usr/bin/perl
use List::MoreUtils qw(apply);
my @input = ( "a.txt" , "b.txt" , "c.txt" ) ;
my @output = apply { s/\..*$// } @input ;
print join(' ', @output), "\n";
或将其定义复制到您的代码中:
sub apply (&@) {
my $action = shift;
&$action foreach my @values = @_;
wantarray ? @values : $values[-1];
}